diff --git a/senlin/common/sdk.py b/senlin/common/sdk.py index 3908b81cc..f1b153b2d 100644 --- a/senlin/common/sdk.py +++ b/senlin/common/sdk.py @@ -13,13 +13,157 @@ ''' SDK Client ''' + +from oslo_serialization import jsonutils + from openstack import connection from openstack import exceptions from openstack import user_preference +from requests import exceptions as reqexc USER_AGENT = 'senlin' exc = exceptions +verbose = False + + +class BaseException(Exception): + '''An error occurred.''' + def __init__(self, message=None): + self.message = message + + def __str__(self): + return self.message or self.__class__.__doc__ + + +class HTTPException(BaseException): + """Base exception for all HTTP-derived exceptions.""" + code = 'N/A' + + def __init__(self, error=None): + super(HTTPException, self).__init__(error) + try: + self.error = error + if 'error' not in self.error: + raise KeyError(_('Key "error" not exists')) + except KeyError: + # If key 'error' does not exist, self.message becomes + # no sense. In this case, we return doc of current + # exception class instead. + self.error = {'error': {'message': self.__class__.__doc__}} + except Exception: + self.error = {'error': + {'message': self.message or self.__class__.__doc__}} + + def __str__(self): + message = self.error['error'].get('message', 'Internal Error') + if verbose: + traceback = self.error['error'].get('traceback', '') + return (_('ERROR: %(message)s\n%(traceback)s') % + {'message': message, 'traceback': traceback}) + else: + code = self.error['error'].get('code', 'Unknown') + return _('ERROR(%(code)s): %(message)s') % {'code': code, + 'message': message} + + +class ClientError(HTTPException): + pass + + +class ServerError(HTTPException): + pass + + +class ConnectionRefused(HTTPException): + # 111 + pass + + +class HTTPBadRequest(ClientError): + # 400 + pass + + +class HTTPUnauthorized(ClientError): + # 401 + pass + + +class HTTPForbidden(ClientError): + # 403 + pass + + +class HTTPNotFound(ClientError): + # 404 + pass + + +class HTTPInternalServerError(ServerError): + # 500 + pass + + +class HTTPNotImplemented(ServerError): + # 501 + pass + + +class HTTPServiceUnavailable(ServerError): + # 503 + pass + + +_EXCEPTION_MAP = { + 111: ConnectionRefused, + 400: HTTPBadRequest, + 401: HTTPUnauthorized, + 403: HTTPForbidden, + 404: HTTPNotFound, + 500: HTTPInternalServerError, + 501: HTTPNotImplemented, + 503: HTTPServiceUnavailable, +} + + +def parse_exception(ex): + '''Parse exception code and yield useful information. + :param details: details of the exception. + ''' + if isinstance(ex, exc.HttpException): + record = jsonutils.loads(ex.details) + elif isinstance(ex, reqexc.RequestException): + # Exceptions that are not captured by SDK + code = ex.message[1].errno + record = { + 'error': { + 'code': code, + 'message': ex.message[0], + } + } + else: + print(_('Unknown exception: %s') % ex) + return + + try: + code = record['error']['code'] + except KeyError as err: + print(_('Malformed exception record, missing field "%s"') % err) + print(_('Original error record: %s') % record) + return + + if code in _EXCEPTION_MAP: + inst = _EXCEPTION_MAP.get(code) + return inst(record) + else: + return HTTPException(record) + + +def ignore_not_found(ex): + parsed = parse_exception(ex) + if not isinstance(parsed, HTTPNotFound): + raise parsed def create_connection(context):