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)
 | 
