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.
28
from ironic.openstack.common.gettextutils import _
31
class ClientException(Exception):
32
"""The base exception class for all exceptions this library raises.
37
class MissingArgs(ClientException):
38
"""Supplied arguments are not sufficient for calling a function."""
39
def __init__(self, missing):
40
self.missing = missing
41
msg = _("Missing arguments: %s") % ", ".join(missing)
42
super(MissingArgs, self).__init__(msg)
45
class ValidationError(ClientException):
46
"""Error in validation on API client side."""
50
class UnsupportedVersion(ClientException):
51
"""User is trying to use an unsupported version of the API."""
55
class CommandError(ClientException):
56
"""Error in CLI tool."""
60
class AuthorizationFailure(ClientException):
61
"""Cannot authorize API client."""
65
class ConnectionRefused(ClientException):
66
"""Cannot connect to API service."""
70
class AuthPluginOptionsMissing(AuthorizationFailure):
71
"""Auth plugin misses some options."""
72
def __init__(self, opt_names):
73
super(AuthPluginOptionsMissing, self).__init__(
74
_("Authentication failed. Missing options: %s") %
76
self.opt_names = opt_names
79
class AuthSystemNotFound(AuthorizationFailure):
80
"""User has specified an AuthSystem that is not installed."""
81
def __init__(self, auth_system):
82
super(AuthSystemNotFound, self).__init__(
83
_("AuthSystemNotFound: %s") % repr(auth_system))
84
self.auth_system = auth_system
87
class NoUniqueMatch(ClientException):
88
"""Multiple entities found instead of one."""
92
class EndpointException(ClientException):
93
"""Something is rotten in Service Catalog."""
97
class EndpointNotFound(EndpointException):
98
"""Could not find requested endpoint in Service Catalog."""
102
class AmbiguousEndpoints(EndpointException):
103
"""Found more than one matching endpoint in Service Catalog."""
104
def __init__(self, endpoints=None):
105
super(AmbiguousEndpoints, self).__init__(
106
_("AmbiguousEndpoints: %s") % repr(endpoints))
107
self.endpoints = endpoints
110
class HttpError(ClientException):
111
"""The base exception class for all HTTP exceptions.
114
message = _("HTTP Error")
116
def __init__(self, message=None, details=None,
117
response=None, request_id=None,
118
url=None, method=None, http_status=None):
119
self.http_status = http_status or self.http_status
120
self.message = message or self.message
121
self.details = details
122
self.request_id = request_id
123
self.response = response
126
formatted_string = "%s (HTTP %s)" % (self.message, self.http_status)
128
formatted_string += " (Request-ID: %s)" % request_id
129
super(HttpError, self).__init__(formatted_string)
132
class HTTPRedirection(HttpError):
133
"""HTTP Redirection."""
134
message = _("HTTP Redirection")
137
class HTTPClientError(HttpError):
138
"""Client-side HTTP error.
140
Exception for cases in which the client seems to have erred.
142
message = _("HTTP Client Error")
145
class HttpServerError(HttpError):
146
"""Server-side HTTP error.
148
Exception for cases in which the server is aware that it has
149
erred or is incapable of performing the request.
151
message = _("HTTP Server Error")
154
class MultipleChoices(HTTPRedirection):
155
"""HTTP 300 - Multiple Choices.
157
Indicates multiple options for the resource that the client may follow.
161
message = _("Multiple Choices")
164
class BadRequest(HTTPClientError):
165
"""HTTP 400 - Bad Request.
167
The request cannot be fulfilled due to bad syntax.
170
message = _("Bad Request")
173
class Unauthorized(HTTPClientError):
174
"""HTTP 401 - Unauthorized.
176
Similar to 403 Forbidden, but specifically for use when authentication
177
is required and has failed or has not yet been provided.
180
message = _("Unauthorized")
183
class PaymentRequired(HTTPClientError):
184
"""HTTP 402 - Payment Required.
186
Reserved for future use.
189
message = _("Payment Required")
192
class Forbidden(HTTPClientError):
193
"""HTTP 403 - Forbidden.
195
The request was a valid request, but the server is refusing to respond
199
message = _("Forbidden")
202
class NotFound(HTTPClientError):
203
"""HTTP 404 - Not Found.
205
The requested resource could not be found but may be available again
209
message = _("Not Found")
212
class MethodNotAllowed(HTTPClientError):
213
"""HTTP 405 - Method Not Allowed.
215
A request was made of a resource using a request method not supported
219
message = _("Method Not Allowed")
222
class NotAcceptable(HTTPClientError):
223
"""HTTP 406 - Not Acceptable.
225
The requested resource is only capable of generating content not
226
acceptable according to the Accept headers sent in the request.
229
message = _("Not Acceptable")
232
class ProxyAuthenticationRequired(HTTPClientError):
233
"""HTTP 407 - Proxy Authentication Required.
235
The client must first authenticate itself with the proxy.
238
message = _("Proxy Authentication Required")
241
class RequestTimeout(HTTPClientError):
242
"""HTTP 408 - Request Timeout.
244
The server timed out waiting for the request.
247
message = _("Request Timeout")
250
class Conflict(HTTPClientError):
251
"""HTTP 409 - Conflict.
253
Indicates that the request could not be processed because of conflict
254
in the request, such as an edit conflict.
257
message = _("Conflict")
260
class Gone(HTTPClientError):
263
Indicates that the resource requested is no longer available and will
264
not be available again.
270
class LengthRequired(HTTPClientError):
271
"""HTTP 411 - Length Required.
273
The request did not specify the length of its content, which is
274
required by the requested resource.
277
message = _("Length Required")
280
class PreconditionFailed(HTTPClientError):
281
"""HTTP 412 - Precondition Failed.
283
The server does not meet one of the preconditions that the requester
287
message = _("Precondition Failed")
290
class RequestEntityTooLarge(HTTPClientError):
291
"""HTTP 413 - Request Entity Too Large.
293
The request is larger than the server is willing or able to process.
296
message = _("Request Entity Too Large")
298
def __init__(self, *args, **kwargs):
300
self.retry_after = int(kwargs.pop('retry_after'))
301
except (KeyError, ValueError):
304
super(RequestEntityTooLarge, self).__init__(*args, **kwargs)
307
class RequestUriTooLong(HTTPClientError):
308
"""HTTP 414 - Request-URI Too Long.
310
The URI provided was too long for the server to process.
313
message = _("Request-URI Too Long")
316
class UnsupportedMediaType(HTTPClientError):
317
"""HTTP 415 - Unsupported Media Type.
319
The request entity has a media type which the server or resource does
323
message = _("Unsupported Media Type")
326
class RequestedRangeNotSatisfiable(HTTPClientError):
327
"""HTTP 416 - Requested Range Not Satisfiable.
329
The client has asked for a portion of the file, but the server cannot
333
message = _("Requested Range Not Satisfiable")
336
class ExpectationFailed(HTTPClientError):
337
"""HTTP 417 - Expectation Failed.
339
The server cannot meet the requirements of the Expect request-header field.
342
message = _("Expectation Failed")
345
class UnprocessableEntity(HTTPClientError):
346
"""HTTP 422 - Unprocessable Entity.
348
The request was well-formed but was unable to be followed due to semantic
352
message = _("Unprocessable Entity")
355
class InternalServerError(HttpServerError):
356
"""HTTP 500 - Internal Server Error.
358
A generic error message, given when no more specific message is suitable.
361
message = _("Internal Server Error")
364
# NotImplemented is a python keyword.
365
class HttpNotImplemented(HttpServerError):
366
"""HTTP 501 - Not Implemented.
368
The server either does not recognize the request method, or it lacks
369
the ability to fulfill the request.
372
message = _("Not Implemented")
375
class BadGateway(HttpServerError):
376
"""HTTP 502 - Bad Gateway.
378
The server was acting as a gateway or proxy and received an invalid
379
response from the upstream server.
382
message = _("Bad Gateway")
385
class ServiceUnavailable(HttpServerError):
386
"""HTTP 503 - Service Unavailable.
388
The server is currently unavailable.
391
message = _("Service Unavailable")
394
class GatewayTimeout(HttpServerError):
395
"""HTTP 504 - Gateway Timeout.
397
The server was acting as a gateway or proxy and did not receive a timely
398
response from the upstream server.
401
message = _("Gateway Timeout")
404
class HttpVersionNotSupported(HttpServerError):
405
"""HTTP 505 - HttpVersion Not Supported.
407
The server does not support the HTTP protocol version used in the request.
410
message = _("HTTP Version Not Supported")
413
# _code_map contains all the classes that have http_status attribute.
415
(getattr(obj, 'http_status', None), obj)
416
for name, obj in six.iteritems(vars(sys.modules[__name__]))
417
if inspect.isclass(obj) and getattr(obj, 'http_status', False)
421
def from_response(response, method, url):
422
"""Returns an instance of :class:`HttpError` or subclass based on response.
424
:param response: instance of `requests.Response` class
425
:param method: HTTP method used for request
426
:param url: URL used for request
429
req_id = response.headers.get("x-openstack-request-id")
430
# NOTE(hdd) true for older versions of nova and cinder
432
req_id = response.headers.get("x-compute-request-id")
434
"http_status": response.status_code,
435
"response": response,
438
"request_id": req_id,
440
if "retry-after" in response.headers:
441
kwargs["retry_after"] = response.headers["retry-after"]
443
content_type = response.headers.get("Content-Type", "")
444
if content_type.startswith("application/json"):
446
body = response.json()
450
if isinstance(body, dict):
451
error = list(body.values())[0]
452
kwargs["message"] = error.get("message")
453
kwargs["details"] = error.get("details")
454
elif content_type.startswith("text/"):
455
kwargs["details"] = response.text
458
cls = _code_map[response.status_code]
460
if 500 <= response.status_code < 600:
461
cls = HttpServerError
462
elif 400 <= response.status_code < 500:
463
cls = HTTPClientError