1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2012 Nicira, Inc.
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
10
# http://www.apache.org/licenses/LICENSE-2.0
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
18
# @author: Somik Behera, Nicira Networks, Inc.
20
import httplib # basic HTTP library for HTTPS connections
22
from quantum.plugins.nicira.api_client import (
23
client_eventlet, request_eventlet)
25
LOG = logging.getLogger("NVPApiHelper")
26
LOG.setLevel(logging.INFO)
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 ()):
33
if header_name == 'server':
34
return header_value.split('/')[1]
36
LOG.warning(_("Unable to fetch NVP version from response "
37
"headers:%s"), headers)
40
class NVPApiHelper(client_eventlet.NvpApiClientEventlet):
43
Helper class to do basic login, cookie management, and provide base
44
method to send HTTP requests.
46
Implements new eventlet-based framework derived from the management
47
console nvp_gevent_client module.
50
def __init__(self, api_providers, user, password, request_timeout,
51
http_timeout, retries, redirects,
52
concurrent_connections=3, nvp_gen_timeout=-1):
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
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.
69
client_eventlet.NvpApiClientEventlet.__init__(
70
self, api_providers, user, password, concurrent_connections,
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
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.
84
Assumes same password is used for all controllers.
86
:param user: NVP controller user (usually admin). Provided for
87
backwards compatability. In the normal mode of operation
89
:param password: NVP controller password. Provided for backwards
90
compatability. In the normal mode of operation this should
93
:returns: Does not return a value.
98
self._password = password
100
return client_eventlet.NvpApiClientEventlet._login(self)
102
def request(self, method, url, body="", content_type="application/json"):
103
'''Issues request to controller.'''
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)
112
LOG.debug(_('NVPApiHelper.request() returns "%s"'), response)
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
125
LOG.error(_('Request timed out: %(method)s to %(url)s'),
126
{'method': method, 'url': url})
127
raise RequestTimeout()
129
status = response.status
130
if status == httplib.UNAUTHORIZED:
131
raise UnAuthorizedRequest()
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)
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})
149
if not self._nvp_version:
150
self._nvp_version = _find_nvp_version_in_headers(response.headers)
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
162
def fourZeroFour(self):
163
raise ResourceNotFound()
165
def fourZeroNine(self):
168
def fiveZeroThree(self):
169
raise ServiceUnavailable()
171
def fourZeroThree(self):
175
raise NvpApiException()
177
# TODO(del): ensure error_codes are handled/raised appropriately
179
error_codes = {404: fourZeroFour,
190
class NvpApiException(Exception):
191
"""Base NvpApiClient Exception.
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.
198
message = _("An unknown exception occurred.")
200
def __init__(self, **kwargs):
202
self._error_string = self.message % kwargs
205
# at least get the core message out if something happened
206
self._error_string = self.message
209
return self._error_string
212
class UnAuthorizedRequest(NvpApiException):
213
message = _("Server denied session's authentication credentials.")
216
class ResourceNotFound(NvpApiException):
217
message = _("An entity referenced in the request was not found.")
220
class Conflict(NvpApiException):
221
message = _("Request conflicts with configuration on a different "
225
class ServiceUnavailable(NvpApiException):
226
message = _("Request could not completed because the associated "
227
"resource could not be reached.")
230
class Forbidden(NvpApiException):
231
message = _("The request is forbidden from accessing the "
232
"referenced resource.")
235
class RequestTimeout(NvpApiException):
236
message = _("The request has timed out.")