Major revision to client side exception handling

Revisions include:
1. Make client side HttpException accept a SDK HttpException in its
   constructor.
2. Print error code when printing error messages.
3. Added ClientError and ServerError and subclasses based on their
   HTTP code.
4. Added parse_exception() method to pase the exception based on its
   error code so that proper error message is shown.
This commit is contained in:
tengqm
2015-01-25 18:54:59 +08:00
parent a100d79e68
commit 8ae4a00501

View File

@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import six
from oslo_serialization import jsonutils
from openstack import exceptions as sdkexc
@@ -39,17 +41,17 @@ class HTTPException(BaseException):
"""Base exception for all HTTP-derived exceptions."""
code = 'N/A'
def __init__(self, details=None):
super(HTTPException, self).__init__(details)
def __init__(self, error=None):
super(HTTPException, self).__init__(error)
try:
self.error = jsonutils.loads(details)
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__}}
self.error = {'error': {'message': self.__class__.__doc__}}
except Exception:
self.error = {'error':
{'message': self.message or self.__class__.__doc__}}
@@ -61,7 +63,186 @@ class HTTPException(BaseException):
return (_('ERROR: %(message)s\n%(traceback)s') %
{'message': message, 'traceback': traceback})
else:
return _('ERROR: %s') % message
code = self.error['error'].get('code', 'Unknown')
return _('ERROR(%s): %s') % (code, message)
class HTTPNotFound(HTTPException):
code = '404'
class ClientError(HTTPException):
pass
class ServerError(HTTPException):
pass
class HTTPBadRequest(ClientError):
# 400
pass
class HTTPUnauthorized(ClientError):
# 401
pass
class HTTPForbidden(ClientError):
# 403
pass
class HTTPNotFound(ClientError):
# 404
pass
class HTTPMethodNotAllowed(ClientError):
# 405
pass
class HTTPNotAcceptable(ClientError):
# 406
pass
class HTTPProxyAuthenticationRequired(ClientError):
# 407
pass
class HTTPRequestTimeout(ClientError):
# 408
pass
class HTTPConflict(ClientError):
# 409
pass
class HTTPGone(ClientError):
# 410
pass
class HTTPLengthRequired(ClientError):
# 411
pass
class HTTPPreconditionFailed(ClientError):
# 412
pass
class HTTPRequestEntityTooLarge(ClientError):
# 413
pass
class HTTPRequestURITooLong(ClientError):
# 414
pass
class HTTPUnsupportedMediaType(ClientError):
# 415
pass
class HTTPRequestRangeNotSatisfiable(ClientError):
# 416
pass
class HTTPExpectationFailed(ClientError):
# 417
pass
class HTTPInternalServerError(ServerError):
# 500
pass
class HTTPNotImplemented(ServerError):
# 501
pass
class HTTPBadGateway(ServerError):
# 502
pass
class HTTPServiceUnavailable(ServerError):
# 503
pass
class HTTPGatewayTimeout(ServerError):
# 504
pass
class HTTPVersionNotSupported(ServerError):
# 505
pass
_EXCEPTION_MAP = {
400: HTTPBadRequest,
401: HTTPUnauthorized,
403: HTTPForbidden,
404: HTTPNotFound,
405: HTTPMethodNotAllowed,
406: HTTPNotAcceptable,
407: HTTPProxyAuthenticationRequired,
408: HTTPRequestTimeout,
409: HTTPConflict,
410: HTTPGone,
411: HTTPLengthRequired,
412: HTTPPreconditionFailed,
413: HTTPRequestEntityTooLarge,
414: HTTPRequestURITooLong,
415: HTTPUnsupportedMediaType,
416: HTTPRequestRangeNotSatisfiable,
417: HTTPExpectationFailed,
500: HTTPInternalServerError,
501: HTTPNotImplemented,
502: HTTPBadGateway,
503: HTTPServiceUnavailable,
504: HTTPGatewayTimeout,
505: HTTPVersionNotSupported,
}
def parse_exception(exc):
'''Parse exception code and yield useful information.
:param details: details of the exception.
'''
if isinstance(exc, sdkexc.HttpException):
record = jsonutils.loads(exc.details)
elif isinstance(exc, six.string_types):
record = jsonutils.loads(exc)
else:
print(_('Unknown exception: %s') % exc)
return
try:
code = record['error']['code']
except KeyError as err:
# Some exception are not caught by SDK, we need to try again
# The 'code' field may be misplaced
try:
code = record['code']
record['error']['code'] = 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)
raise inst(record)
else:
raise HTTPException(record)