1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2010 Jacob Kaplan-Moss
4
# Copyright 2011 Nebula, Inc.
5
# Copyright 2013 Alessio Ababilov
6
# Copyright 2013 OpenStack Foundation
9
# Licensed under the Apache License, Version 2.0 (the "License"); you may
10
# not use this file except in compliance with the License. You may obtain
11
# a copy of the License at
13
# http://www.apache.org/licenses/LICENSE-2.0
15
# Unless required by applicable law or agreed to in writing, software
16
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18
# License for the specific language governing permissions and limitations
22
Exception definitions.
28
class ClientException(Exception):
29
"""The base exception class for all exceptions this library raises.
34
class MissingArgs(ClientException):
35
"""Supplied arguments are not sufficient for calling a function."""
36
def __init__(self, missing):
37
self.missing = missing
38
msg = "Missing argument(s): %s" % ", ".join(missing)
39
super(MissingArgs, self).__init__(msg)
42
class ValidationError(ClientException):
43
"""Error in validation on API client side."""
47
class UnsupportedVersion(ClientException):
48
"""User is trying to use an unsupported version of the API."""
52
class CommandError(ClientException):
53
"""Error in CLI tool."""
57
class AuthorizationFailure(ClientException):
58
"""Cannot authorize API client."""
62
class AuthPluginOptionsMissing(AuthorizationFailure):
63
"""Auth plugin misses some options."""
64
def __init__(self, opt_names):
65
super(AuthPluginOptionsMissing, self).__init__(
66
"Authentication failed. Missing options: %s" %
68
self.opt_names = opt_names
71
class AuthSystemNotFound(AuthorizationFailure):
72
"""User has specified a AuthSystem that is not installed."""
73
def __init__(self, auth_system):
74
super(AuthSystemNotFound, self).__init__(
75
"AuthSystemNotFound: %s" % repr(auth_system))
76
self.auth_system = auth_system
79
class NoUniqueMatch(ClientException):
80
"""Multiple entities found instead of one."""
84
class EndpointException(ClientException):
85
"""Something is rotten in Service Catalog."""
89
class EndpointNotFound(EndpointException):
90
"""Could not find requested endpoint in Service Catalog."""
94
class EmptyCatalog(EndpointNotFound):
95
"""The service catalog is empty."""
99
class AmbiguousEndpoints(EndpointException):
100
"""Found more than one matching endpoint in Service Catalog."""
101
def __init__(self, endpoints=None):
102
super(AmbiguousEndpoints, self).__init__(
103
"AmbiguousEndpoints: %s" % repr(endpoints))
104
self.endpoints = endpoints
107
class HTTPError(ClientException):
108
"""The base exception class for all HTTP exceptions.
111
message = "HTTP Error"
113
def __init__(self, message=None, details=None,
114
response=None, request_id=None,
115
url=None, method=None, http_status=None):
116
self.http_status = http_status or self.http_status
117
self.message = message or self.message
118
self.details = details
119
self.request_id = request_id
120
self.response = response
123
formatted_string = "%(message)s (HTTP %(status)s)" % {
124
"message": self.message, "status": self.http_status}
126
formatted_string += " (Request-ID: %s)" % request_id
127
super(HTTPError, self).__init__(formatted_string)
130
class HTTPClientError(HTTPError):
131
"""Client-side HTTP error.
133
Exception for cases in which the client seems to have erred.
135
message = "HTTP Client Error"
138
class HTTPServerError(HTTPError):
139
"""Server-side HTTP error.
141
Exception for cases in which the server is aware that it has
142
erred or is incapable of performing the request.
144
message = "HTTP Server Error"
147
class BadRequest(HTTPClientError):
148
"""HTTP 400 - Bad Request.
150
The request cannot be fulfilled due to bad syntax.
153
message = "Bad Request"
156
class Unauthorized(HTTPClientError):
157
"""HTTP 401 - Unauthorized.
159
Similar to 403 Forbidden, but specifically for use when authentication
160
is required and has failed or has not yet been provided.
163
message = "Unauthorized"
166
class PaymentRequired(HTTPClientError):
167
"""HTTP 402 - Payment Required.
169
Reserved for future use.
172
message = "Payment Required"
175
class Forbidden(HTTPClientError):
176
"""HTTP 403 - Forbidden.
178
The request was a valid request, but the server is refusing to respond
182
message = "Forbidden"
185
class NotFound(HTTPClientError):
186
"""HTTP 404 - Not Found.
188
The requested resource could not be found but may be available again
192
message = "Not Found"
195
class MethodNotAllowed(HTTPClientError):
196
"""HTTP 405 - Method Not Allowed.
198
A request was made of a resource using a request method not supported
202
message = "Method Not Allowed"
205
class NotAcceptable(HTTPClientError):
206
"""HTTP 406 - Not Acceptable.
208
The requested resource is only capable of generating content not
209
acceptable according to the Accept headers sent in the request.
212
message = "Not Acceptable"
215
class ProxyAuthenticationRequired(HTTPClientError):
216
"""HTTP 407 - Proxy Authentication Required.
218
The client must first authenticate itself with the proxy.
221
message = "Proxy Authentication Required"
224
class RequestTimeout(HTTPClientError):
225
"""HTTP 408 - Request Timeout.
227
The server timed out waiting for the request.
230
message = "Request Timeout"
233
class Conflict(HTTPClientError):
234
"""HTTP 409 - Conflict.
236
Indicates that the request could not be processed because of conflict
237
in the request, such as an edit conflict.
243
class Gone(HTTPClientError):
246
Indicates that the resource requested is no longer available and will
247
not be available again.
253
class LengthRequired(HTTPClientError):
254
"""HTTP 411 - Length Required.
256
The request did not specify the length of its content, which is
257
required by the requested resource.
260
message = "Length Required"
263
class PreconditionFailed(HTTPClientError):
264
"""HTTP 412 - Precondition Failed.
266
The server does not meet one of the preconditions that the requester
270
message = "Precondition Failed"
273
class RequestEntityTooLarge(HTTPClientError):
274
"""HTTP 413 - Request Entity Too Large.
276
The request is larger than the server is willing or able to process.
279
message = "Request Entity Too Large"
281
def __init__(self, *args, **kwargs):
283
self.retry_after = int(kwargs.pop('retry_after'))
284
except (KeyError, ValueError):
287
super(RequestEntityTooLarge, self).__init__(*args, **kwargs)
290
class RequestUriTooLong(HTTPClientError):
291
"""HTTP 414 - Request-URI Too Long.
293
The URI provided was too long for the server to process.
296
message = "Request-URI Too Long"
299
class UnsupportedMediaType(HTTPClientError):
300
"""HTTP 415 - Unsupported Media Type.
302
The request entity has a media type which the server or resource does
306
message = "Unsupported Media Type"
309
class RequestedRangeNotSatisfiable(HTTPClientError):
310
"""HTTP 416 - Requested Range Not Satisfiable.
312
The client has asked for a portion of the file, but the server cannot
316
message = "Requested Range Not Satisfiable"
319
class ExpectationFailed(HTTPClientError):
320
"""HTTP 417 - Expectation Failed.
322
The server cannot meet the requirements of the Expect request-header field.
325
message = "Expectation Failed"
328
class UnprocessableEntity(HTTPClientError):
329
"""HTTP 422 - Unprocessable Entity.
331
The request was well-formed but was unable to be followed due to semantic
335
message = "Unprocessable Entity"
338
class InternalServerError(HTTPServerError):
339
"""HTTP 500 - Internal Server Error.
341
A generic error message, given when no more specific message is suitable.
344
message = "Internal Server Error"
347
# NotImplemented is a python keyword.
348
class HTTPNotImplemented(HTTPServerError):
349
"""HTTP 501 - Not Implemented.
351
The server either does not recognize the request method, or it lacks
352
the ability to fulfill the request.
355
message = "Not Implemented"
358
class BadGateway(HTTPServerError):
359
"""HTTP 502 - Bad Gateway.
361
The server was acting as a gateway or proxy and received an invalid
362
response from the upstream server.
365
message = "Bad Gateway"
368
class ServiceUnavailable(HTTPServerError):
369
"""HTTP 503 - Service Unavailable.
371
The server is currently unavailable.
374
message = "Service Unavailable"
377
class GatewayTimeout(HTTPServerError):
378
"""HTTP 504 - Gateway Timeout.
380
The server was acting as a gateway or proxy and did not receive a timely
381
response from the upstream server.
384
message = "Gateway Timeout"
387
class HTTPVersionNotSupported(HTTPServerError):
388
"""HTTP 505 - HTTPVersion Not Supported.
390
The server does not support the HTTP protocol version used in the request.
393
message = "HTTP Version Not Supported"
397
(cls.http_status, cls)
398
for cls in itertools.chain(HTTPClientError.__subclasses__(),
399
HTTPServerError.__subclasses__()))
402
def from_response(response, method, url):
403
"""Returns an instance of :class:`HTTPError` or subclass based on response.
405
:param response: instance of `requests.Response` class
406
:param method: HTTP method used for request
407
:param url: URL used for request
410
"http_status": response.status_code,
411
"response": response,
414
"request_id": response.headers.get("x-compute-request-id"),
416
if "retry-after" in response.headers:
417
kwargs["retry_after"] = response.headers["retry-after"]
419
content_type = response.headers.get("Content-Type", "")
420
if content_type.startswith("application/json"):
422
body = response.json()
426
if hasattr(body, "keys"):
427
error = body[body.keys()[0]]
428
kwargs["message"] = error.get("message")
429
kwargs["details"] = error.get("details")
430
elif content_type.startswith("text/"):
431
kwargs["details"] = response.text
434
cls = _code_map[response.status_code]
436
if 500 <= response.status_code < 600:
437
cls = HTTPServerError
438
elif 400 <= response.status_code < 500:
439
cls = HTTPClientError