Merge "Send HTTP exceptions in the format expected by neutronclient"
This commit is contained in:
commit
35b2d27883
|
@ -99,17 +99,16 @@ def Resource(controller, faults=None, deserializers=None, serializers=None):
|
||||||
else:
|
else:
|
||||||
LOG.exception(_('%s failed'), action)
|
LOG.exception(_('%s failed'), action)
|
||||||
e = translate(e, language)
|
e = translate(e, language)
|
||||||
# following structure is expected by python-neutronclient
|
body = serializer.serialize(
|
||||||
err_data = {'type': e.__class__.__name__,
|
{'NeutronError': get_exception_data(e)})
|
||||||
'message': e, 'detail': ''}
|
|
||||||
body = serializer.serialize({'NeutronError': err_data})
|
|
||||||
kwargs = {'body': body, 'content_type': content_type}
|
kwargs = {'body': body, 'content_type': content_type}
|
||||||
raise mapped_exc(**kwargs)
|
raise mapped_exc(**kwargs)
|
||||||
except webob.exc.HTTPException as e:
|
except webob.exc.HTTPException as e:
|
||||||
type_, value, tb = sys.exc_info()
|
type_, value, tb = sys.exc_info()
|
||||||
LOG.exception(_('%s failed'), action)
|
LOG.exception(_('%s failed'), action)
|
||||||
translate(e, language)
|
translate(e, language)
|
||||||
value.body = serializer.serialize({'NeutronError': e})
|
value.body = serializer.serialize(
|
||||||
|
{'NeutronError': get_exception_data(e)})
|
||||||
value.content_type = content_type
|
value.content_type = content_type
|
||||||
six.reraise(type_, value, tb)
|
six.reraise(type_, value, tb)
|
||||||
except NotImplementedError as e:
|
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,
|
# because a plugin does not implement a feature,
|
||||||
# returning 500 is definitely confusing.
|
# returning 500 is definitely confusing.
|
||||||
body = serializer.serialize(
|
body = serializer.serialize(
|
||||||
{'NotImplementedError': e.message})
|
{'NotImplementedError': get_exception_data(e)})
|
||||||
kwargs = {'body': body, 'content_type': content_type}
|
kwargs = {'body': body, 'content_type': content_type}
|
||||||
raise webob.exc.HTTPNotImplemented(**kwargs)
|
raise webob.exc.HTTPNotImplemented(**kwargs)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -131,7 +130,9 @@ def Resource(controller, faults=None, deserializers=None, serializers=None):
|
||||||
msg = _('Request Failed: internal server error while '
|
msg = _('Request Failed: internal server error while '
|
||||||
'processing your request.')
|
'processing your request.')
|
||||||
msg = translate(msg, language)
|
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}
|
kwargs = {'body': body, 'content_type': content_type}
|
||||||
raise webob.exc.HTTPInternalServerError(**kwargs)
|
raise webob.exc.HTTPInternalServerError(**kwargs)
|
||||||
|
|
||||||
|
@ -148,6 +149,21 @@ def Resource(controller, faults=None, deserializers=None, serializers=None):
|
||||||
return resource
|
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):
|
def translate(translatable, locale):
|
||||||
"""Translates the object to the given locale.
|
"""Translates the object to the given locale.
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,13 @@ class RequestTestCase(base.BaseTestCase):
|
||||||
|
|
||||||
class ResourceTestCase(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):
|
def test_unmapped_neutron_error_with_json(self):
|
||||||
msg = u'\u7f51\u7edc'
|
msg = u'\u7f51\u7edc'
|
||||||
|
|
||||||
|
@ -260,47 +267,74 @@ class ResourceTestCase(base.BaseTestCase):
|
||||||
self.assertIn(msg_translation,
|
self.assertIn(msg_translation,
|
||||||
str(wsgi.JSONDeserializer().deserialize(res.body)))
|
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 = mock.MagicMock()
|
||||||
controller.test.side_effect = exc.HTTPGatewayTimeout()
|
controller.test.side_effect = side_effect
|
||||||
|
|
||||||
resource = webtest.TestApp(wsgi_resource.Resource(controller))
|
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)
|
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):
|
def test_unhandled_error_with_json(self):
|
||||||
expected_res = {'body': {'NeutronError':
|
self._test_unhandled_error()
|
||||||
_('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)
|
|
||||||
|
|
||||||
def test_unhandled_error_with_xml(self):
|
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':
|
expected_res = {'body': {'NeutronError':
|
||||||
_('Request Failed: internal server error '
|
{'detail': '',
|
||||||
'while processing your request.')}}
|
'message': _(
|
||||||
controller = mock.MagicMock()
|
'The server has either erred or is '
|
||||||
controller.test.side_effect = Exception()
|
'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',
|
def test_not_implemented_error_with_json(self):
|
||||||
'format': 'xml'})}
|
self._test_not_implemented_error()
|
||||||
res = resource.get('', extra_environ=environ, expect_errors=True)
|
|
||||||
self.assertEqual(res.status_int, exc.HTTPInternalServerError.code)
|
def test_not_implemented_error_with_xml(self):
|
||||||
self.assertEqual(wsgi.XMLDeserializer().deserialize(res.body),
|
self._test_not_implemented_error(req_format='xml')
|
||||||
expected_res)
|
|
||||||
|
|
||||||
def test_status_200(self):
|
def test_status_200(self):
|
||||||
controller = mock.MagicMock()
|
controller = mock.MagicMock()
|
||||||
|
|
Loading…
Reference in New Issue