271 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			271 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# -*- coding: utf-8 -*-
 | 
						|
 | 
						|
# 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 classes and logic for cratonclient."""
 | 
						|
 | 
						|
 | 
						|
class ClientException(Exception):
 | 
						|
    """Base exception class for all exceptions in cratonclient."""
 | 
						|
 | 
						|
    message = None
 | 
						|
 | 
						|
    def __init__(self, message=None):
 | 
						|
        """Initialize our exception instance with our class level message."""
 | 
						|
        if message is None:
 | 
						|
            if self.message is None:
 | 
						|
                message = self.__class__.__name__
 | 
						|
            else:
 | 
						|
                message = self.message
 | 
						|
        super(ClientException, self).__init__(message)
 | 
						|
 | 
						|
 | 
						|
class Timeout(ClientException):
 | 
						|
    """Catch-all class for connect and read timeouts from requests."""
 | 
						|
 | 
						|
    message = 'Request timed out'
 | 
						|
 | 
						|
    def __init__(self, message=None, **kwargs):
 | 
						|
        """Initialize our Timeout exception.
 | 
						|
 | 
						|
        This takes an optional keyword-only argument of
 | 
						|
        ``original_exception``.
 | 
						|
        """
 | 
						|
        self.original_exception = kwargs.pop('exception', None)
 | 
						|
        super(Timeout, self).__init__(message)
 | 
						|
 | 
						|
 | 
						|
class HTTPError(ClientException):
 | 
						|
    """Base exception class for all HTTP related exceptions in."""
 | 
						|
 | 
						|
    message = "An error occurred while talking to the remote server."
 | 
						|
    status_code = None
 | 
						|
 | 
						|
    def __init__(self, message=None, **kwargs):
 | 
						|
        """Initialize our HTTPError instance.
 | 
						|
 | 
						|
        Optional keyword-only arguments include:
 | 
						|
 | 
						|
        - response: for the response generating the error
 | 
						|
        - original_exception: in the event that this is a requests exception
 | 
						|
          that we are re-raising.
 | 
						|
        """
 | 
						|
        self.response = kwargs.pop('response', None)
 | 
						|
        self.original_exception = kwargs.pop('exception', None)
 | 
						|
        self.status_code = (self.status_code
 | 
						|
                            or getattr(self.response, 'status_code', None))
 | 
						|
        super(HTTPError, self).__init__(message)
 | 
						|
 | 
						|
    @property
 | 
						|
    def status_code(self):
 | 
						|
        """Shim to provide a similar API to other OpenStack clients."""
 | 
						|
        return self.status_code
 | 
						|
 | 
						|
    @status_code.setter
 | 
						|
    def status_code(self, code):
 | 
						|
        self.status_code = code
 | 
						|
 | 
						|
 | 
						|
class ConnectionFailed(HTTPError):
 | 
						|
    """Connecting to the server failed."""
 | 
						|
 | 
						|
    message = "An error occurred while connecting to the server."""
 | 
						|
 | 
						|
 | 
						|
class HTTPClientError(HTTPError):
 | 
						|
    """Base exception for client side errors (4xx status codes)."""
 | 
						|
 | 
						|
    message = "Something went wrong with the request."""
 | 
						|
 | 
						|
 | 
						|
class BadRequest(HTTPClientError):
 | 
						|
    """Client sent a malformed request."""
 | 
						|
 | 
						|
    status_code = 400
 | 
						|
    message = "The request sent to the server was invalid."
 | 
						|
 | 
						|
 | 
						|
class Unauthorized(HTTPClientError):
 | 
						|
    """Client is unauthorized to access the resource in question."""
 | 
						|
 | 
						|
    status_code = 401
 | 
						|
    message = ("The user has either provided insufficient parameters for "
 | 
						|
               "authentication or is not authorized to access this resource.")
 | 
						|
 | 
						|
 | 
						|
class Forbidden(HTTPClientError):
 | 
						|
    """Client is forbidden to access the resource."""
 | 
						|
 | 
						|
    status_code = 403
 | 
						|
    message = ("The user was unable to access the resource because they are "
 | 
						|
               "forbidden.")
 | 
						|
 | 
						|
 | 
						|
class NotFound(HTTPClientError):
 | 
						|
    """Resource could not be found."""
 | 
						|
 | 
						|
    status_code = 404
 | 
						|
    message = "The requested resource was not found."""
 | 
						|
 | 
						|
 | 
						|
class MethodNotAllowed(HTTPClientError):
 | 
						|
    """The request method is not supported."""
 | 
						|
 | 
						|
    status_code = 405
 | 
						|
    message = "The method used in the request is not supported."
 | 
						|
 | 
						|
 | 
						|
class NotAcceptable(HTTPClientError):
 | 
						|
    """The requested resource can not respond with acceptable content.
 | 
						|
 | 
						|
    Based on the Accept headers specified by the client, the resource can not
 | 
						|
    generate content that is an acceptable content-type.
 | 
						|
    """
 | 
						|
 | 
						|
    status_code = 406
 | 
						|
    message = "The resource can not return acceptable content."
 | 
						|
 | 
						|
 | 
						|
class ProxyAuthenticationRequired(HTTPClientError):
 | 
						|
    """The client must first authenticate itself with the proxy."""
 | 
						|
 | 
						|
    status_code = 407
 | 
						|
    message = "The client must first authenticate itself with a proxy."
 | 
						|
 | 
						|
 | 
						|
class Conflict(HTTPClientError):
 | 
						|
    """The request presents a conflict."""
 | 
						|
 | 
						|
    status_code = 409
 | 
						|
    message = "The request could not be processed due to a conflict."
 | 
						|
 | 
						|
 | 
						|
class Gone(HTTPClientError):
 | 
						|
    """The requested resource is no longer available.
 | 
						|
 | 
						|
    The resource requested is no longer available and will not be available
 | 
						|
    again.
 | 
						|
    """
 | 
						|
 | 
						|
    status_code = 410
 | 
						|
    message = ("The resource requested is no longer available and will not be"
 | 
						|
               " available again.")
 | 
						|
 | 
						|
 | 
						|
class LengthRequired(HTTPClientError):
 | 
						|
    """The request did not specify a Content-Length header."""
 | 
						|
 | 
						|
    status_code = 411
 | 
						|
    message = ("The request did not contain a Content-Length header but one"
 | 
						|
               " was required by the resource.")
 | 
						|
 | 
						|
 | 
						|
class PreconditionFailed(HTTPClientError):
 | 
						|
    """The server failed to meet one of the preconditions in the request."""
 | 
						|
 | 
						|
    status_code = 412
 | 
						|
    message = ("The server failed to meet one of the preconditions in the "
 | 
						|
               "request.")
 | 
						|
 | 
						|
 | 
						|
class RequestEntityTooLarge(HTTPClientError):
 | 
						|
    """The request is larger than the server is willing or able to process."""
 | 
						|
 | 
						|
    status_code = 413
 | 
						|
    message = ("The request is larger than the server is willing or able to "
 | 
						|
               "process.")
 | 
						|
 | 
						|
 | 
						|
class RequestUriTooLong(HTTPClientError):
 | 
						|
    """The URI provided was too long for the server to process."""
 | 
						|
 | 
						|
    status_code = 414
 | 
						|
    message = "The URI provided was too long for the server to process."
 | 
						|
 | 
						|
 | 
						|
class UnsupportedMediaType(HTTPClientError):
 | 
						|
    """The request entity has a media type which is unsupported."""
 | 
						|
 | 
						|
    status_code = 415
 | 
						|
    message = ("The request entity has a media type which is unsupported by "
 | 
						|
               "the server or resource.")
 | 
						|
 | 
						|
 | 
						|
class RequestedRangeNotSatisfiable(HTTPClientError):
 | 
						|
    """The requestor wanted a range but the server was unable to provide it."""
 | 
						|
 | 
						|
    status_code = 416
 | 
						|
    message = ("The requestor wanted a range but the server was unable to "
 | 
						|
               "provide it.")
 | 
						|
 | 
						|
 | 
						|
class UnprocessableEntity(HTTPClientError):
 | 
						|
    """There were semantic errors in the request."""
 | 
						|
 | 
						|
    status_code = 422
 | 
						|
    message = ("The request is of a valid content-type and structure but "
 | 
						|
               "semantically invalid.")
 | 
						|
 | 
						|
 | 
						|
_4xx_classes = [
 | 
						|
    BadRequest,
 | 
						|
    Unauthorized,
 | 
						|
    Forbidden,
 | 
						|
    NotFound,
 | 
						|
    MethodNotAllowed,
 | 
						|
    NotAcceptable,
 | 
						|
    ProxyAuthenticationRequired,
 | 
						|
    Conflict,
 | 
						|
    Gone,
 | 
						|
    LengthRequired,
 | 
						|
    PreconditionFailed,
 | 
						|
    RequestEntityTooLarge,
 | 
						|
    RequestUriTooLong,
 | 
						|
    UnsupportedMediaType,
 | 
						|
    RequestedRangeNotSatisfiable,
 | 
						|
    UnprocessableEntity,
 | 
						|
]
 | 
						|
_4xx_codes = {cls.status_code: cls for cls in _4xx_classes}
 | 
						|
 | 
						|
 | 
						|
class HTTPServerError(HTTPError):
 | 
						|
    """The server encountered an error it could not recover from."""
 | 
						|
 | 
						|
    message = "HTTP Server-side Error"
 | 
						|
 | 
						|
 | 
						|
class InternalServerError(HTTPServerError):
 | 
						|
    """The server encountered an error it could not recover from."""
 | 
						|
 | 
						|
    status_code = 500
 | 
						|
    message = ("There was an internal server error that could not be recovered"
 | 
						|
               " from.")
 | 
						|
 | 
						|
 | 
						|
_5xx_classes = [
 | 
						|
    InternalServerError,
 | 
						|
    # NOTE(sigmavirus24): Allow for future expansion
 | 
						|
]
 | 
						|
_5xx_codes = {cls.status_code: cls for cls in _5xx_classes}
 | 
						|
 | 
						|
 | 
						|
def error_from(response):
 | 
						|
    """Find an error code that matches a response status_code."""
 | 
						|
    if 400 <= response.status_code < 500:
 | 
						|
        cls = _4xx_codes.get(response.status_code, HTTPClientError)
 | 
						|
    elif 500 <= response.status_code < 600:
 | 
						|
        cls = _5xx_codes.get(response.status_code, HTTPServerError)
 | 
						|
    else:
 | 
						|
        cls = HTTPError
 | 
						|
 | 
						|
    return cls(response=response)
 |