add escalator cli framework
[escalator.git] / client / escalatorclient / openstack / common / apiclient / exceptions.py
diff --git a/client/escalatorclient/openstack/common/apiclient/exceptions.py b/client/escalatorclient/openstack/common/apiclient/exceptions.py
new file mode 100644 (file)
index 0000000..bcda21d
--- /dev/null
@@ -0,0 +1,479 @@
+# Copyright 2010 Jacob Kaplan-Moss
+# Copyright 2011 Nebula, Inc.
+# Copyright 2013 Alessio Ababilov
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+"""
+Exception definitions.
+"""
+
+########################################################################
+#
+# THIS MODULE IS DEPRECATED
+#
+# Please refer to
+# https://etherpad.openstack.org/p/kilo-escalatorclient-library-proposals for
+# the discussion leading to this deprecation.
+#
+# We recommend checking out the python-openstacksdk project
+# (https://launchpad.net/python-openstacksdk) instead.
+#
+########################################################################
+
+import inspect
+import sys
+
+import six
+
+from escalatorclient.openstack.common._i18n import _
+
+
+class ClientException(Exception):
+    """The base exception class for all exceptions this library raises.
+    """
+    pass
+
+
+class ValidationError(ClientException):
+    """Error in validation on API client side."""
+    pass
+
+
+class UnsupportedVersion(ClientException):
+    """User is trying to use an unsupported version of the API."""
+    pass
+
+
+class CommandError(ClientException):
+    """Error in CLI tool."""
+    pass
+
+
+class AuthorizationFailure(ClientException):
+    """Cannot authorize API client."""
+    pass
+
+
+class ConnectionError(ClientException):
+    """Cannot connect to API service."""
+    pass
+
+
+class ConnectionRefused(ConnectionError):
+    """Connection refused while trying to connect to API service."""
+    pass
+
+
+class AuthPluginOptionsMissing(AuthorizationFailure):
+    """Auth plugin misses some options."""
+    def __init__(self, opt_names):
+        super(AuthPluginOptionsMissing, self).__init__(
+            _("Authentication failed. Missing options: %s") %
+            ", ".join(opt_names))
+        self.opt_names = opt_names
+
+
+class AuthSystemNotFound(AuthorizationFailure):
+    """User has specified an AuthSystem that is not installed."""
+    def __init__(self, auth_system):
+        super(AuthSystemNotFound, self).__init__(
+            _("AuthSystemNotFound: %r") % auth_system)
+        self.auth_system = auth_system
+
+
+class NoUniqueMatch(ClientException):
+    """Multiple entities found instead of one."""
+    pass
+
+
+class EndpointException(ClientException):
+    """Something is rotten in Service Catalog."""
+    pass
+
+
+class EndpointNotFound(EndpointException):
+    """Could not find requested endpoint in Service Catalog."""
+    pass
+
+
+class AmbiguousEndpoints(EndpointException):
+    """Found more than one matching endpoint in Service Catalog."""
+    def __init__(self, endpoints=None):
+        super(AmbiguousEndpoints, self).__init__(
+            _("AmbiguousEndpoints: %r") % endpoints)
+        self.endpoints = endpoints
+
+
+class HttpError(ClientException):
+    """The base exception class for all HTTP exceptions.
+    """
+    http_status = 0
+    message = _("HTTP Error")
+
+    def __init__(self, message=None, details=None,
+                 response=None, request_id=None,
+                 url=None, method=None, http_status=None):
+        self.http_status = http_status or self.http_status
+        self.message = message or self.message
+        self.details = details
+        self.request_id = request_id
+        self.response = response
+        self.url = url
+        self.method = method
+        formatted_string = "%s (HTTP %s)" % (self.message, self.http_status)
+        if request_id:
+            formatted_string += " (Request-ID: %s)" % request_id
+        super(HttpError, self).__init__(formatted_string)
+
+
+class HTTPRedirection(HttpError):
+    """HTTP Redirection."""
+    message = _("HTTP Redirection")
+
+
+class HTTPClientError(HttpError):
+    """Client-side HTTP error.
+
+    Exception for cases in which the client seems to have erred.
+    """
+    message = _("HTTP Client Error")
+
+
+class HttpServerError(HttpError):
+    """Server-side HTTP error.
+
+    Exception for cases in which the server is aware that it has
+    erred or is incapable of performing the request.
+    """
+    message = _("HTTP Server Error")
+
+
+class MultipleChoices(HTTPRedirection):
+    """HTTP 300 - Multiple Choices.
+
+    Indicates multiple options for the resource that the client may follow.
+    """
+
+    http_status = 300
+    message = _("Multiple Choices")
+
+
+class BadRequest(HTTPClientError):
+    """HTTP 400 - Bad Request.
+
+    The request cannot be fulfilled due to bad syntax.
+    """
+    http_status = 400
+    message = _("Bad Request")
+
+
+class Unauthorized(HTTPClientError):
+    """HTTP 401 - Unauthorized.
+
+    Similar to 403 Forbidden, but specifically for use when authentication
+    is required and has failed or has not yet been provided.
+    """
+    http_status = 401
+    message = _("Unauthorized")
+
+
+class PaymentRequired(HTTPClientError):
+    """HTTP 402 - Payment Required.
+
+    Reserved for future use.
+    """
+    http_status = 402
+    message = _("Payment Required")
+
+
+class Forbidden(HTTPClientError):
+    """HTTP 403 - Forbidden.
+
+    The request was a valid request, but the server is refusing to respond
+    to it.
+    """
+    http_status = 403
+    message = _("Forbidden")
+
+
+class NotFound(HTTPClientError):
+    """HTTP 404 - Not Found.
+
+    The requested resource could not be found but may be available again
+    in the future.
+    """
+    http_status = 404
+    message = _("Not Found")
+
+
+class MethodNotAllowed(HTTPClientError):
+    """HTTP 405 - Method Not Allowed.
+
+    A request was made of a resource using a request method not supported
+    by that resource.
+    """
+    http_status = 405
+    message = _("Method Not Allowed")
+
+
+class NotAcceptable(HTTPClientError):
+    """HTTP 406 - Not Acceptable.
+
+    The requested resource is only capable of generating content not
+    acceptable according to the Accept headers sent in the request.
+    """
+    http_status = 406
+    message = _("Not Acceptable")
+
+
+class ProxyAuthenticationRequired(HTTPClientError):
+    """HTTP 407 - Proxy Authentication Required.
+
+    The client must first authenticate itself with the proxy.
+    """
+    http_status = 407
+    message = _("Proxy Authentication Required")
+
+
+class RequestTimeout(HTTPClientError):
+    """HTTP 408 - Request Timeout.
+
+    The server timed out waiting for the request.
+    """
+    http_status = 408
+    message = _("Request Timeout")
+
+
+class Conflict(HTTPClientError):
+    """HTTP 409 - Conflict.
+
+    Indicates that the request could not be processed because of conflict
+    in the request, such as an edit conflict.
+    """
+    http_status = 409
+    message = _("Conflict")
+
+
+class Gone(HTTPClientError):
+    """HTTP 410 - Gone.
+
+    Indicates that the resource requested is no longer available and will
+    not be available again.
+    """
+    http_status = 410
+    message = _("Gone")
+
+
+class LengthRequired(HTTPClientError):
+    """HTTP 411 - Length Required.
+
+    The request did not specify the length of its content, which is
+    required by the requested resource.
+    """
+    http_status = 411
+    message = _("Length Required")
+
+
+class PreconditionFailed(HTTPClientError):
+    """HTTP 412 - Precondition Failed.
+
+    The server does not meet one of the preconditions that the requester
+    put on the request.
+    """
+    http_status = 412
+    message = _("Precondition Failed")
+
+
+class RequestEntityTooLarge(HTTPClientError):
+    """HTTP 413 - Request Entity Too Large.
+
+    The request is larger than the server is willing or able to process.
+    """
+    http_status = 413
+    message = _("Request Entity Too Large")
+
+    def __init__(self, *args, **kwargs):
+        try:
+            self.retry_after = int(kwargs.pop('retry_after'))
+        except (KeyError, ValueError):
+            self.retry_after = 0
+
+        super(RequestEntityTooLarge, self).__init__(*args, **kwargs)
+
+
+class RequestUriTooLong(HTTPClientError):
+    """HTTP 414 - Request-URI Too Long.
+
+    The URI provided was too long for the server to process.
+    """
+    http_status = 414
+    message = _("Request-URI Too Long")
+
+
+class UnsupportedMediaType(HTTPClientError):
+    """HTTP 415 - Unsupported Media Type.
+
+    The request entity has a media type which the server or resource does
+    not support.
+    """
+    http_status = 415
+    message = _("Unsupported Media Type")
+
+
+class RequestedRangeNotSatisfiable(HTTPClientError):
+    """HTTP 416 - Requested Range Not Satisfiable.
+
+    The client has asked for a portion of the file, but the server cannot
+    supply that portion.
+    """
+    http_status = 416
+    message = _("Requested Range Not Satisfiable")
+
+
+class ExpectationFailed(HTTPClientError):
+    """HTTP 417 - Expectation Failed.
+
+    The server cannot meet the requirements of the Expect request-header field.
+    """
+    http_status = 417
+    message = _("Expectation Failed")
+
+
+class UnprocessableEntity(HTTPClientError):
+    """HTTP 422 - Unprocessable Entity.
+
+    The request was well-formed but was unable to be followed due to semantic
+    errors.
+    """
+    http_status = 422
+    message = _("Unprocessable Entity")
+
+
+class InternalServerError(HttpServerError):
+    """HTTP 500 - Internal Server Error.
+
+    A generic error message, given when no more specific message is suitable.
+    """
+    http_status = 500
+    message = _("Internal Server Error")
+
+
+# NotImplemented is a python keyword.
+class HttpNotImplemented(HttpServerError):
+    """HTTP 501 - Not Implemented.
+
+    The server either does not recognize the request method, or it lacks
+    the ability to fulfill the request.
+    """
+    http_status = 501
+    message = _("Not Implemented")
+
+
+class BadGateway(HttpServerError):
+    """HTTP 502 - Bad Gateway.
+
+    The server was acting as a gateway or proxy and received an invalid
+    response from the upstream server.
+    """
+    http_status = 502
+    message = _("Bad Gateway")
+
+
+class ServiceUnavailable(HttpServerError):
+    """HTTP 503 - Service Unavailable.
+
+    The server is currently unavailable.
+    """
+    http_status = 503
+    message = _("Service Unavailable")
+
+
+class GatewayTimeout(HttpServerError):
+    """HTTP 504 - Gateway Timeout.
+
+    The server was acting as a gateway or proxy and did not receive a timely
+    response from the upstream server.
+    """
+    http_status = 504
+    message = _("Gateway Timeout")
+
+
+class HttpVersionNotSupported(HttpServerError):
+    """HTTP 505 - HttpVersion Not Supported.
+
+    The server does not support the HTTP protocol version used in the request.
+    """
+    http_status = 505
+    message = _("HTTP Version Not Supported")
+
+
+# _code_map contains all the classes that have http_status attribute.
+_code_map = dict(
+    (getattr(obj, 'http_status', None), obj)
+    for name, obj in six.iteritems(vars(sys.modules[__name__]))
+    if inspect.isclass(obj) and getattr(obj, 'http_status', False)
+)
+
+
+def from_response(response, method, url):
+    """Returns an instance of :class:`HttpError` or subclass based on response.
+
+    :param response: instance of `requests.Response` class
+    :param method: HTTP method used for request
+    :param url: URL used for request
+    """
+
+    req_id = response.headers.get("x-openstack-request-id")
+    # NOTE(hdd) true for older versions of nova and cinder
+    if not req_id:
+        req_id = response.headers.get("x-compute-request-id")
+    kwargs = {
+        "http_status": response.status_code,
+        "response": response,
+        "method": method,
+        "url": url,
+        "request_id": req_id,
+    }
+    if "retry-after" in response.headers:
+        kwargs["retry_after"] = response.headers["retry-after"]
+
+    content_type = response.headers.get("Content-Type", "")
+    if content_type.startswith("application/json"):
+        try:
+            body = response.json()
+        except ValueError:
+            pass
+        else:
+            if isinstance(body, dict):
+                error = body.get(list(body)[0])
+                if isinstance(error, dict):
+                    kwargs["message"] = (error.get("message") or
+                                         error.get("faultstring"))
+                    kwargs["details"] = (error.get("details") or
+                                         six.text_type(body))
+    elif content_type.startswith("text/"):
+        kwargs["details"] = response.text
+
+    try:
+        cls = _code_map[response.status_code]
+    except KeyError:
+        if 500 <= response.status_code < 600:
+            cls = HttpServerError
+        elif 400 <= response.status_code < 500:
+            cls = HTTPClientError
+        else:
+            cls = HttpError
+    return cls(**kwargs)