1 # Copyright 2010 Jacob Kaplan-Moss
2 # Copyright 2011 Nebula, Inc.
3 # Copyright 2013 Alessio Ababilov
4 # Copyright 2013 OpenStack Foundation
7 # Licensed under the Apache License, Version 2.0 (the "License"); you may
8 # not use this file except in compliance with the License. You may obtain
9 # a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 # License for the specific language governing permissions and limitations
20 Exception definitions.
23 ########################################################################
25 # THIS MODULE IS DEPRECATED
28 # https://etherpad.openstack.org/p/kilo-escalatorclient-library-proposals for
29 # the discussion leading to this deprecation.
31 # We recommend checking out the python-openstacksdk project
32 # (https://launchpad.net/python-openstacksdk) instead.
34 ########################################################################
41 from escalatorclient.openstack.common._i18n import _
44 class ClientException(Exception):
45 """The base exception class for all exceptions this library raises.
50 class ValidationError(ClientException):
51 """Error in validation on API client side."""
55 class UnsupportedVersion(ClientException):
56 """User is trying to use an unsupported version of the API."""
60 class CommandError(ClientException):
61 """Error in CLI tool."""
65 class AuthorizationFailure(ClientException):
66 """Cannot authorize API client."""
70 class ConnectionError(ClientException):
71 """Cannot connect to API service."""
75 class ConnectionRefused(ConnectionError):
76 """Connection refused while trying to connect to API service."""
80 class AuthPluginOptionsMissing(AuthorizationFailure):
81 """Auth plugin misses some options."""
82 def __init__(self, opt_names):
83 super(AuthPluginOptionsMissing, self).__init__(
84 _("Authentication failed. Missing options: %s") %
86 self.opt_names = opt_names
89 class AuthSystemNotFound(AuthorizationFailure):
90 """User has specified an AuthSystem that is not installed."""
91 def __init__(self, auth_system):
92 super(AuthSystemNotFound, self).__init__(
93 _("AuthSystemNotFound: %r") % auth_system)
94 self.auth_system = auth_system
97 class NoUniqueMatch(ClientException):
98 """Multiple entities found instead of one."""
102 class EndpointException(ClientException):
103 """Something is rotten in Service Catalog."""
107 class EndpointNotFound(EndpointException):
108 """Could not find requested endpoint in Service Catalog."""
112 class AmbiguousEndpoints(EndpointException):
113 """Found more than one matching endpoint in Service Catalog."""
114 def __init__(self, endpoints=None):
115 super(AmbiguousEndpoints, self).__init__(
116 _("AmbiguousEndpoints: %r") % endpoints)
117 self.endpoints = endpoints
120 class HttpError(ClientException):
121 """The base exception class for all HTTP exceptions.
124 message = _("HTTP Error")
126 def __init__(self, message=None, details=None,
127 response=None, request_id=None,
128 url=None, method=None, http_status=None):
129 self.http_status = http_status or self.http_status
130 self.message = message or self.message
131 self.details = details
132 self.request_id = request_id
133 self.response = response
136 formatted_string = "%s (HTTP %s)" % (self.message, self.http_status)
138 formatted_string += " (Request-ID: %s)" % request_id
139 super(HttpError, self).__init__(formatted_string)
142 class HTTPRedirection(HttpError):
143 """HTTP Redirection."""
144 message = _("HTTP Redirection")
147 class HTTPClientError(HttpError):
148 """Client-side HTTP error.
150 Exception for cases in which the client seems to have erred.
152 message = _("HTTP Client Error")
155 class HttpServerError(HttpError):
156 """Server-side HTTP error.
158 Exception for cases in which the server is aware that it has
159 erred or is incapable of performing the request.
161 message = _("HTTP Server Error")
164 class MultipleChoices(HTTPRedirection):
165 """HTTP 300 - Multiple Choices.
167 Indicates multiple options for the resource that the client may follow.
171 message = _("Multiple Choices")
174 class BadRequest(HTTPClientError):
175 """HTTP 400 - Bad Request.
177 The request cannot be fulfilled due to bad syntax.
180 message = _("Bad Request")
183 class Unauthorized(HTTPClientError):
184 """HTTP 401 - Unauthorized.
186 Similar to 403 Forbidden, but specifically for use when authentication
187 is required and has failed or has not yet been provided.
190 message = _("Unauthorized")
193 class PaymentRequired(HTTPClientError):
194 """HTTP 402 - Payment Required.
196 Reserved for future use.
199 message = _("Payment Required")
202 class Forbidden(HTTPClientError):
203 """HTTP 403 - Forbidden.
205 The request was a valid request, but the server is refusing to respond
209 message = _("Forbidden")
212 class NotFound(HTTPClientError):
213 """HTTP 404 - Not Found.
215 The requested resource could not be found but may be available again
219 message = _("Not Found")
222 class MethodNotAllowed(HTTPClientError):
223 """HTTP 405 - Method Not Allowed.
225 A request was made of a resource using a request method not supported
229 message = _("Method Not Allowed")
232 class NotAcceptable(HTTPClientError):
233 """HTTP 406 - Not Acceptable.
235 The requested resource is only capable of generating content not
236 acceptable according to the Accept headers sent in the request.
239 message = _("Not Acceptable")
242 class ProxyAuthenticationRequired(HTTPClientError):
243 """HTTP 407 - Proxy Authentication Required.
245 The client must first authenticate itself with the proxy.
248 message = _("Proxy Authentication Required")
251 class RequestTimeout(HTTPClientError):
252 """HTTP 408 - Request Timeout.
254 The server timed out waiting for the request.
257 message = _("Request Timeout")
260 class Conflict(HTTPClientError):
261 """HTTP 409 - Conflict.
263 Indicates that the request could not be processed because of conflict
264 in the request, such as an edit conflict.
267 message = _("Conflict")
270 class Gone(HTTPClientError):
273 Indicates that the resource requested is no longer available and will
274 not be available again.
280 class LengthRequired(HTTPClientError):
281 """HTTP 411 - Length Required.
283 The request did not specify the length of its content, which is
284 required by the requested resource.
287 message = _("Length Required")
290 class PreconditionFailed(HTTPClientError):
291 """HTTP 412 - Precondition Failed.
293 The server does not meet one of the preconditions that the requester
297 message = _("Precondition Failed")
300 class RequestEntityTooLarge(HTTPClientError):
301 """HTTP 413 - Request Entity Too Large.
303 The request is larger than the server is willing or able to process.
306 message = _("Request Entity Too Large")
308 def __init__(self, *args, **kwargs):
310 self.retry_after = int(kwargs.pop('retry_after'))
311 except (KeyError, ValueError):
314 super(RequestEntityTooLarge, self).__init__(*args, **kwargs)
317 class RequestUriTooLong(HTTPClientError):
318 """HTTP 414 - Request-URI Too Long.
320 The URI provided was too long for the server to process.
323 message = _("Request-URI Too Long")
326 class UnsupportedMediaType(HTTPClientError):
327 """HTTP 415 - Unsupported Media Type.
329 The request entity has a media type which the server or resource does
333 message = _("Unsupported Media Type")
336 class RequestedRangeNotSatisfiable(HTTPClientError):
337 """HTTP 416 - Requested Range Not Satisfiable.
339 The client has asked for a portion of the file, but the server cannot
343 message = _("Requested Range Not Satisfiable")
346 class ExpectationFailed(HTTPClientError):
347 """HTTP 417 - Expectation Failed.
349 The server cannot meet the requirements of the Expect request-header field.
352 message = _("Expectation Failed")
355 class UnprocessableEntity(HTTPClientError):
356 """HTTP 422 - Unprocessable Entity.
358 The request was well-formed but was unable to be followed due to semantic
362 message = _("Unprocessable Entity")
365 class InternalServerError(HttpServerError):
366 """HTTP 500 - Internal Server Error.
368 A generic error message, given when no more specific message is suitable.
371 message = _("Internal Server Error")
374 # NotImplemented is a python keyword.
375 class HttpNotImplemented(HttpServerError):
376 """HTTP 501 - Not Implemented.
378 The server either does not recognize the request method, or it lacks
379 the ability to fulfill the request.
382 message = _("Not Implemented")
385 class BadGateway(HttpServerError):
386 """HTTP 502 - Bad Gateway.
388 The server was acting as a gateway or proxy and received an invalid
389 response from the upstream server.
392 message = _("Bad Gateway")
395 class ServiceUnavailable(HttpServerError):
396 """HTTP 503 - Service Unavailable.
398 The server is currently unavailable.
401 message = _("Service Unavailable")
404 class GatewayTimeout(HttpServerError):
405 """HTTP 504 - Gateway Timeout.
407 The server was acting as a gateway or proxy and did not receive a timely
408 response from the upstream server.
411 message = _("Gateway Timeout")
414 class HttpVersionNotSupported(HttpServerError):
415 """HTTP 505 - HttpVersion Not Supported.
417 The server does not support the HTTP protocol version used in the request.
420 message = _("HTTP Version Not Supported")
423 # _code_map contains all the classes that have http_status attribute.
425 (getattr(obj, 'http_status', None), obj)
426 for name, obj in six.iteritems(vars(sys.modules[__name__]))
427 if inspect.isclass(obj) and getattr(obj, 'http_status', False)
431 def from_response(response, method, url):
432 """Returns an instance of :class:`HttpError` or subclass based on response.
434 :param response: instance of `requests.Response` class
435 :param method: HTTP method used for request
436 :param url: URL used for request
439 req_id = response.headers.get("x-openstack-request-id")
440 # NOTE(hdd) true for older versions of nova and cinder
442 req_id = response.headers.get("x-compute-request-id")
444 "http_status": response.status_code,
445 "response": response,
448 "request_id": req_id,
450 if "retry-after" in response.headers:
451 kwargs["retry_after"] = response.headers["retry-after"]
453 content_type = response.headers.get("Content-Type", "")
454 if content_type.startswith("application/json"):
456 body = response.json()
460 if isinstance(body, dict):
461 error = body.get(list(body)[0])
462 if isinstance(error, dict):
463 kwargs["message"] = (error.get("message") or
464 error.get("faultstring"))
465 kwargs["details"] = (error.get("details") or
467 elif content_type.startswith("text/"):
468 kwargs["details"] = response.text
471 cls = _code_map[response.status_code]
473 if 500 <= response.status_code < 600:
474 cls = HttpServerError
475 elif 400 <= response.status_code < 500:
476 cls = HTTPClientError