Send HTTP exceptions in the format expected by neutronclient
Neutron client for the v2 API expects exceptions to have 'type', 'message' and 'detail' attributes. That is why they need to be included in the body of HTTP Exceptions. Change-Id: I70bd47977e42ad7bac760600329e9440452b74bc Closes-Bug: 1355902
This commit is contained in:
parent
f907677f1d
commit
ea3c43a402
@ -99,17 +99,16 @@ def Resource(controller, faults=None, deserializers=None, serializers=None):
|
||||
else:
|
||||
LOG.exception(_('%s failed'), action)
|
||||
e = translate(e, language)
|
||||
# following structure is expected by python-neutronclient
|
||||
err_data = {'type': e.__class__.__name__,
|
||||
'message': e, 'detail': ''}
|
||||
body = serializer.serialize({'NeutronError': err_data})
|
||||
body = serializer.serialize(
|
||||
{'NeutronError': get_exception_data(e)})
|
||||
kwargs = {'body': body, 'content_type': content_type}
|
||||
raise mapped_exc(**kwargs)
|
||||
except webob.exc.HTTPException as e:
|
||||
type_, value, tb = sys.exc_info()
|
||||
LOG.exception(_('%s failed'), action)
|
||||
translate(e, language)
|
||||
value.body = serializer.serialize({'NeutronError': e})
|
||||
value.body = serializer.serialize(
|
||||
{'NeutronError': get_exception_data(e)})
|
||||
value.content_type = content_type
|
||||
six.reraise(type_, value, tb)
|
||||
except NotImplementedError as e:
|
||||
@ -121,7 +120,7 @@ def Resource(controller, faults=None, deserializers=None, serializers=None):
|
||||
# because a plugin does not implement a feature,
|
||||
# returning 500 is definitely confusing.
|
||||
body = serializer.serialize(
|
||||
{'NotImplementedError': e.message})
|
||||
{'NotImplementedError': get_exception_data(e)})
|
||||
kwargs = {'body': body, 'content_type': content_type}
|
||||
raise webob.exc.HTTPNotImplemented(**kwargs)
|
||||
except Exception:
|
||||
@ -131,7 +130,9 @@ def Resource(controller, faults=None, deserializers=None, serializers=None):
|
||||
msg = _('Request Failed: internal server error while '
|
||||
'processing your request.')
|
||||
msg = translate(msg, language)
|
||||
body = serializer.serialize({'NeutronError': msg})
|
||||
body = serializer.serialize(
|
||||
{'NeutronError': get_exception_data(
|
||||
webob.exc.HTTPInternalServerError(msg))})
|
||||
kwargs = {'body': body, 'content_type': content_type}
|
||||
raise webob.exc.HTTPInternalServerError(**kwargs)
|
||||
|
||||
@ -148,6 +149,21 @@ def Resource(controller, faults=None, deserializers=None, serializers=None):
|
||||
return resource
|
||||
|
||||
|
||||
def get_exception_data(e):
|
||||
"""Extract the information about an exception.
|
||||
|
||||
Neutron client for the v2 API expects exceptions to have 'type', 'message'
|
||||
and 'detail' attributes.This information is extracted and converted into a
|
||||
dictionary.
|
||||
|
||||
:param e: the exception to be reraised
|
||||
:returns: a structured dict with the exception data
|
||||
"""
|
||||
err_data = {'type': e.__class__.__name__,
|
||||
'message': e, 'detail': ''}
|
||||
return err_data
|
||||
|
||||
|
||||
def translate(translatable, locale):
|
||||
"""Translates the object to the given locale.
|
||||
|
||||
|
@ -122,6 +122,13 @@ class RequestTestCase(base.BaseTestCase):
|
||||
|
||||
class ResourceTestCase(base.BaseTestCase):
|
||||
|
||||
@staticmethod
|
||||
def _get_deserializer(req_format):
|
||||
if req_format == 'json':
|
||||
return wsgi.JSONDeserializer()
|
||||
else:
|
||||
return wsgi.XMLDeserializer()
|
||||
|
||||
def test_unmapped_neutron_error_with_json(self):
|
||||
msg = u'\u7f51\u7edc'
|
||||
|
||||
@ -260,47 +267,74 @@ class ResourceTestCase(base.BaseTestCase):
|
||||
self.assertIn(msg_translation,
|
||||
str(wsgi.JSONDeserializer().deserialize(res.body)))
|
||||
|
||||
def test_http_error(self):
|
||||
@staticmethod
|
||||
def _make_request_with_side_effect(side_effect, req_format=None):
|
||||
controller = mock.MagicMock()
|
||||
controller.test.side_effect = exc.HTTPGatewayTimeout()
|
||||
controller.test.side_effect = side_effect
|
||||
|
||||
resource = webtest.TestApp(wsgi_resource.Resource(controller))
|
||||
|
||||
environ = {'wsgiorg.routing_args': (None, {'action': 'test'})}
|
||||
routing_args = {'action': 'test'}
|
||||
if req_format:
|
||||
routing_args.update({'format': req_format})
|
||||
environ = {'wsgiorg.routing_args': (None, routing_args)}
|
||||
res = resource.get('', extra_environ=environ, expect_errors=True)
|
||||
self.assertEqual(res.status_int, exc.HTTPGatewayTimeout.code)
|
||||
return res
|
||||
|
||||
def test_http_error(self):
|
||||
res = self._make_request_with_side_effect(exc.HTTPGatewayTimeout())
|
||||
|
||||
# verify that the exception structure is the one expected
|
||||
# by the python-neutronclient
|
||||
self.assertEqual(exc.HTTPGatewayTimeout().explanation,
|
||||
res.json['NeutronError']['message'])
|
||||
self.assertEqual('HTTPGatewayTimeout',
|
||||
res.json['NeutronError']['type'])
|
||||
self.assertEqual('', res.json['NeutronError']['detail'])
|
||||
self.assertEqual(exc.HTTPGatewayTimeout.code, res.status_int)
|
||||
|
||||
def _test_unhandled_error(self, req_format='json'):
|
||||
expected_res = {'body': {'NeutronError':
|
||||
{'detail': '',
|
||||
'message': _(
|
||||
'Request Failed: internal server '
|
||||
'error while processing your request.'),
|
||||
'type': 'HTTPInternalServerError'}}}
|
||||
res = self._make_request_with_side_effect(side_effect=Exception(),
|
||||
req_format=req_format)
|
||||
self.assertEqual(exc.HTTPInternalServerError.code,
|
||||
res.status_int)
|
||||
self.assertEqual(expected_res,
|
||||
self._get_deserializer(
|
||||
req_format).deserialize(res.body))
|
||||
|
||||
def test_unhandled_error_with_json(self):
|
||||
expected_res = {'body': {'NeutronError':
|
||||
_('Request Failed: internal server error '
|
||||
'while processing your request.')}}
|
||||
controller = mock.MagicMock()
|
||||
controller.test.side_effect = Exception()
|
||||
|
||||
resource = webtest.TestApp(wsgi_resource.Resource(controller))
|
||||
|
||||
environ = {'wsgiorg.routing_args': (None, {'action': 'test',
|
||||
'format': 'json'})}
|
||||
res = resource.get('', extra_environ=environ, expect_errors=True)
|
||||
self.assertEqual(res.status_int, exc.HTTPInternalServerError.code)
|
||||
self.assertEqual(wsgi.JSONDeserializer().deserialize(res.body),
|
||||
expected_res)
|
||||
self._test_unhandled_error()
|
||||
|
||||
def test_unhandled_error_with_xml(self):
|
||||
self._test_unhandled_error(req_format='xml')
|
||||
|
||||
def _test_not_implemented_error(self, req_format='json'):
|
||||
expected_res = {'body': {'NeutronError':
|
||||
_('Request Failed: internal server error '
|
||||
'while processing your request.')}}
|
||||
controller = mock.MagicMock()
|
||||
controller.test.side_effect = Exception()
|
||||
{'detail': '',
|
||||
'message': _(
|
||||
'The server has either erred or is '
|
||||
'incapable of performing the requested '
|
||||
'operation.'),
|
||||
'type': 'HTTPNotImplemented'}}}
|
||||
|
||||
resource = webtest.TestApp(wsgi_resource.Resource(controller))
|
||||
res = self._make_request_with_side_effect(exc.HTTPNotImplemented(),
|
||||
req_format=req_format)
|
||||
self.assertEqual(exc.HTTPNotImplemented.code, res.status_int)
|
||||
self.assertEqual(expected_res,
|
||||
self._get_deserializer(
|
||||
req_format).deserialize(res.body))
|
||||
|
||||
environ = {'wsgiorg.routing_args': (None, {'action': 'test',
|
||||
'format': 'xml'})}
|
||||
res = resource.get('', extra_environ=environ, expect_errors=True)
|
||||
self.assertEqual(res.status_int, exc.HTTPInternalServerError.code)
|
||||
self.assertEqual(wsgi.XMLDeserializer().deserialize(res.body),
|
||||
expected_res)
|
||||
def test_not_implemented_error_with_json(self):
|
||||
self._test_not_implemented_error()
|
||||
|
||||
def test_not_implemented_error_with_xml(self):
|
||||
self._test_not_implemented_error(req_format='xml')
|
||||
|
||||
def test_status_200(self):
|
||||
controller = mock.MagicMock()
|
||||
|
Loading…
Reference in New Issue
Block a user