text: # Copyright 2010 Jacob Kaplan-Moss
  # Copyright 2011 Nebula, Inc.
  # Copyright 2013 Alessio Ababilov
  # Copyright 2013 OpenStack Foundation
  # (C) Copyright 2014-2015 Hewlett Packard Enterprise Development Company LP
  # 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.
  """
  
  import inspect
  import sys
  
  import six
  
  
  class ClientException(Exception):
      """The base exception class for all exceptions this library raises."""
      pass
  
  
  class MissingArgs(ClientException):
      """Supplied arguments are not sufficient for calling a function."""
      def __init__(self, missing):
          self.missing = missing
          msg = "Missing argument(s): %s" % ", ".join(missing)
          super(MissingArgs, self).__init__(msg)
  
  
  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 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 a AuthSystem that is not installed."""
      def __init__(self, auth_system):
          super(AuthSystemNotFound, self).__init__(
              "AuthSystemNotFound: %s" % repr(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: %s" % repr(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 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 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
      """
      kwargs = {
          "http_status": response.status_code,
          "response": response,
          "method": method,
          "url": url,
          "request_id": response.headers.get("x-compute-request-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 hasattr(body, "keys"):
                  error = body[body.keys()[0]]
                  kwargs["message"] = error.get("message")
                  kwargs["details"] = error.get("details")
      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)
  