~ubuntu-branches/ubuntu/saucy/quantum/saucy

« back to all changes in this revision

Viewing changes to quantum/plugins/nicira/NvpApiClient.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2013-05-31 09:37:25 UTC
  • mfrom: (2.1.22)
  • Revision ID: package-import@ubuntu.com-20130531093725-bf9jom93l7jm57iv
Tags: 1:2013.2~b1-0ubuntu1
* New upstream release.
* debian/patches/fix-quantum-configuration.patch: Refreshed
* debian/control: Add testrepository.
* debian/control: Add subunit.
* debian/control: Add python-d21o1 and python-pbr as build-depends.
* debian/control: Add python-stevedore.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
 
 
3
# Copyright 2012 Nicira, Inc.
 
4
# All Rights Reserved
 
5
#
 
6
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
7
#    not use this file except in compliance with the License. You may obtain
 
8
#    a copy of the License at
 
9
#
 
10
#         http://www.apache.org/licenses/LICENSE-2.0
 
11
#
 
12
#    Unless required by applicable law or agreed to in writing, software
 
13
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
14
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
15
#    License for the specific language governing permissions and limitations
 
16
#    under the License.
 
17
#
 
18
# @author: Somik Behera, Nicira Networks, Inc.
 
19
 
 
20
import httplib  # basic HTTP library for HTTPS connections
 
21
import logging
 
22
from quantum.plugins.nicira.api_client import (
 
23
    client_eventlet, request_eventlet)
 
24
 
 
25
LOG = logging.getLogger("NVPApiHelper")
 
26
LOG.setLevel(logging.INFO)
 
27
 
 
28
 
 
29
def _find_nvp_version_in_headers(headers):
 
30
    # be safe if headers is None - do not cause a failure
 
31
    for (header_name, header_value) in (headers or ()):
 
32
        try:
 
33
            if header_name == 'server':
 
34
                return header_value.split('/')[1]
 
35
        except IndexError:
 
36
            LOG.warning(_("Unable to fetch NVP version from response "
 
37
                          "headers:%s"), headers)
 
38
 
 
39
 
 
40
class NVPApiHelper(client_eventlet.NvpApiClientEventlet):
 
41
    '''API helper class.
 
42
 
 
43
    Helper class to do basic login, cookie management, and provide base
 
44
    method to send HTTP requests.
 
45
 
 
46
    Implements new eventlet-based framework derived from the management
 
47
    console nvp_gevent_client module.
 
48
    '''
 
49
 
 
50
    def __init__(self, api_providers, user, password, request_timeout,
 
51
                 http_timeout, retries, redirects,
 
52
                 concurrent_connections=3, nvp_gen_timeout=-1):
 
53
        '''Constructor.
 
54
 
 
55
        :param api_providers: a list of tuples in the form:
 
56
            (host, port, is_ssl=True). Passed on to NvpClientEventlet.
 
57
        :param user: the login username.
 
58
        :param password: the login password.
 
59
        :param concurrent_connections: the number of concurrent connections.
 
60
        :param request_timeout: all operations (including retries, redirects
 
61
            from unresponsive controllers, etc) should finish within this
 
62
            timeout.
 
63
        :param http_timeout: how long to wait before aborting an
 
64
            unresponsive controller (and allow for retries to another
 
65
            controller in the cluster)
 
66
        :param retries: the number of concurrent connections.
 
67
        :param redirects: the number of concurrent connections.
 
68
        '''
 
69
        client_eventlet.NvpApiClientEventlet.__init__(
 
70
            self, api_providers, user, password, concurrent_connections,
 
71
            nvp_gen_timeout)
 
72
 
 
73
        self._request_timeout = request_timeout
 
74
        self._http_timeout = http_timeout
 
75
        self._retries = retries
 
76
        self._redirects = redirects
 
77
        self._nvp_version = None
 
78
 
 
79
    # NOTE(salvatore-orlando): This method is not used anymore. Login is now
 
80
    # performed automatically inside the request eventlet if necessary.
 
81
    def login(self, user=None, password=None):
 
82
        '''Login to NVP controller.
 
83
 
 
84
        Assumes same password is used for all controllers.
 
85
 
 
86
        :param user: NVP controller user (usually admin). Provided for
 
87
                backwards compatability. In the  normal mode of operation
 
88
                this should be None.
 
89
        :param password: NVP controller password. Provided for backwards
 
90
                compatability. In the normal mode of operation this should
 
91
                be None.
 
92
 
 
93
        :returns: Does not return a value.
 
94
        '''
 
95
        if user:
 
96
            self._user = user
 
97
        if password:
 
98
            self._password = password
 
99
 
 
100
        return client_eventlet.NvpApiClientEventlet._login(self)
 
101
 
 
102
    def request(self, method, url, body="", content_type="application/json"):
 
103
        '''Issues request to controller.'''
 
104
 
 
105
        g = request_eventlet.NvpGenericRequestEventlet(
 
106
            self, method, url, body, content_type, auto_login=True,
 
107
            request_timeout=self._request_timeout,
 
108
            http_timeout=self._http_timeout,
 
109
            retries=self._retries, redirects=self._redirects)
 
110
        g.start()
 
111
        response = g.join()
 
112
        LOG.debug(_('NVPApiHelper.request() returns "%s"'), response)
 
113
 
 
114
        # response is a modified HTTPResponse object or None.
 
115
        # response.read() will not work on response as the underlying library
 
116
        # request_eventlet.NvpApiRequestEventlet has already called this
 
117
        # method in order to extract the body and headers for processing.
 
118
        # NvpApiRequestEventlet derived classes call .read() and
 
119
        # .getheaders() on the HTTPResponse objects and store the results in
 
120
        # the response object's .body and .headers data members for future
 
121
        # access.
 
122
 
 
123
        if response is None:
 
124
            # Timeout.
 
125
            LOG.error(_('Request timed out: %(method)s to %(url)s'),
 
126
                      {'method': method, 'url': url})
 
127
            raise RequestTimeout()
 
128
 
 
129
        status = response.status
 
130
        if status == httplib.UNAUTHORIZED:
 
131
            raise UnAuthorizedRequest()
 
132
 
 
133
        # Fail-fast: Check for exception conditions and raise the
 
134
        # appropriate exceptions for known error codes.
 
135
        if status in self.error_codes:
 
136
            LOG.error(_("Received error code: %s"), status)
 
137
            LOG.error(_("Server Error Message: %s"), response.body)
 
138
            self.error_codes[status](self)
 
139
 
 
140
        # Continue processing for non-error condition.
 
141
        if (status != httplib.OK and status != httplib.CREATED
 
142
                and status != httplib.NO_CONTENT):
 
143
            LOG.error(_("%(method)s to %(url)s, unexpected response code: "
 
144
                        "%(status)d (content = '%(body)s')"),
 
145
                      {'method': method, 'url': url,
 
146
                       'status': response.status, 'body': response.body})
 
147
            return None
 
148
 
 
149
        if not self._nvp_version:
 
150
            self._nvp_version = _find_nvp_version_in_headers(response.headers)
 
151
 
 
152
        return response.body
 
153
 
 
154
    def get_nvp_version(self):
 
155
        if not self._nvp_version:
 
156
            # generate a simple request (/ws.v1/log)
 
157
            # this will cause nvp_version to be fetched
 
158
            # don't bother about response
 
159
            self.request('GET', '/ws.v1/log')
 
160
        return self._nvp_version
 
161
 
 
162
    def fourZeroFour(self):
 
163
        raise ResourceNotFound()
 
164
 
 
165
    def fourZeroNine(self):
 
166
        raise Conflict()
 
167
 
 
168
    def fiveZeroThree(self):
 
169
        raise ServiceUnavailable()
 
170
 
 
171
    def fourZeroThree(self):
 
172
        raise Forbidden()
 
173
 
 
174
    def zero(self):
 
175
        raise NvpApiException()
 
176
 
 
177
    # TODO(del): ensure error_codes are handled/raised appropriately
 
178
    # in api_client.
 
179
    error_codes = {404: fourZeroFour,
 
180
                   409: fourZeroNine,
 
181
                   503: fiveZeroThree,
 
182
                   403: fourZeroThree,
 
183
                   301: zero,
 
184
                   307: zero,
 
185
                   400: zero,
 
186
                   500: zero,
 
187
                   503: zero}
 
188
 
 
189
 
 
190
class NvpApiException(Exception):
 
191
    """Base NvpApiClient Exception.
 
192
 
 
193
    To correctly use this class, inherit from it and define
 
194
    a 'message' property. That message will get printf'd
 
195
    with the keyword arguments provided to the constructor.
 
196
 
 
197
    """
 
198
    message = _("An unknown exception occurred.")
 
199
 
 
200
    def __init__(self, **kwargs):
 
201
        try:
 
202
            self._error_string = self.message % kwargs
 
203
 
 
204
        except Exception:
 
205
            # at least get the core message out if something happened
 
206
            self._error_string = self.message
 
207
 
 
208
    def __str__(self):
 
209
        return self._error_string
 
210
 
 
211
 
 
212
class UnAuthorizedRequest(NvpApiException):
 
213
    message = _("Server denied session's authentication credentials.")
 
214
 
 
215
 
 
216
class ResourceNotFound(NvpApiException):
 
217
    message = _("An entity referenced in the request was not found.")
 
218
 
 
219
 
 
220
class Conflict(NvpApiException):
 
221
    message = _("Request conflicts with configuration on a different "
 
222
                "entity.")
 
223
 
 
224
 
 
225
class ServiceUnavailable(NvpApiException):
 
226
    message = _("Request could not completed because the associated "
 
227
                "resource could not be reached.")
 
228
 
 
229
 
 
230
class Forbidden(NvpApiException):
 
231
    message = _("The request is forbidden from accessing the "
 
232
                "referenced resource.")
 
233
 
 
234
 
 
235
class RequestTimeout(NvpApiException):
 
236
    message = _("The request has timed out.")