Fix required attributes when error happened
Accroding to the NFV-SOL 013v2.6.1 Section 6, modify the API's response body format when error happened. Add title, status and detail attributes and delete problem type, code and message attributes in the response body when error happened. And also change the "Content-Type" from "application/json" to "application/problem+json". Closes-Bug: #1930647 Change-Id: Ic5f08ca924d14a382baeb77470aa3430e317315f
This commit is contained in:
parent
a98cd4eaa9
commit
646554075e
@ -645,8 +645,9 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("No flavour with id 'invalid'.",
|
||||
resp.json['badRequest']['message'])
|
||||
resp.json['detail'])
|
||||
mock_get_vnf.assert_called_once()
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
@ -742,9 +743,10 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("No instantiation level with id "
|
||||
"'instantiation_level_1'.",
|
||||
resp.json['badRequest']['message'])
|
||||
resp.json['detail'])
|
||||
mock_get_vnf.assert_called_once()
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
@ -785,8 +787,9 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("No instantiation level with id 'non-existing'.",
|
||||
resp.json['badRequest']['message'])
|
||||
resp.json['detail'])
|
||||
mock_get_vnf.assert_called_once()
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
@ -887,9 +890,10 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("VimConnection id is not found: %s" %
|
||||
uuidsentinel.vim_id,
|
||||
resp.json['badRequest']['message'])
|
||||
resp.json['detail'])
|
||||
mock_get_vnf.assert_called_once()
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
@ -936,9 +940,10 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("Region not found for the VimConnection: %s" %
|
||||
uuidsentinel.vim_id,
|
||||
resp.json['badRequest']['message'])
|
||||
resp.json['detail'])
|
||||
mock_get_vnf.assert_called_once()
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
@ -979,8 +984,9 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("Default VIM is not defined.",
|
||||
resp.json['badRequest']['message'])
|
||||
resp.json['detail'])
|
||||
mock_get_vnf.assert_called_once()
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
@ -1032,10 +1038,11 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.CONFLICT, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
expected_msg = ("Vnf instance %s in task_state INSTANTIATING. Cannot "
|
||||
"instantiate while the vnf instance is in this state.")
|
||||
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
|
||||
resp.json['conflictingRequest']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1093,8 +1100,9 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("'flavourId' is a required property",
|
||||
resp.json['badRequest']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1134,9 +1142,10 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual(
|
||||
"Can not find requested vnf: %s" % constants.INVALID_UUID,
|
||||
resp.json['itemNotFound']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1159,9 +1168,10 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("Can not find requested vnf instance: %s" %
|
||||
uuidsentinel.vnf_instance_id,
|
||||
resp.json['itemNotFound']['message'])
|
||||
resp.json['detail'])
|
||||
mock_get_vnf.assert_called_once()
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
@ -1261,9 +1271,10 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
|
||||
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("Can not find requested vnf instance: %s" %
|
||||
uuidsentinel.vnf_instance_id,
|
||||
resp.json['itemNotFound']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1275,9 +1286,10 @@ class TestController(base.TestCase):
|
||||
|
||||
resp = req.get_response(self.app)
|
||||
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("Can not find requested vnf instance: %s" %
|
||||
constants.INVALID_UUID,
|
||||
resp.json['itemNotFound']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1385,8 +1397,9 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("'terminationType' is a required property",
|
||||
resp.json['badRequest']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1426,9 +1439,10 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("Can not find requested vnf instance: %s" %
|
||||
uuidsentinel.vnf_instance_id,
|
||||
resp.json['itemNotFound']['message'])
|
||||
resp.json['detail'])
|
||||
mock_get_vnf.assert_called_once()
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
@ -1457,10 +1471,11 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.CONFLICT, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
expected_msg = ("Vnf instance %s in task_state TERMINATING. Cannot "
|
||||
"terminate while the vnf instance is in this state.")
|
||||
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
|
||||
resp.json['conflictingRequest']['message'])
|
||||
resp.json['detail'])
|
||||
mock_get_vnf.assert_called_once()
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
@ -1542,11 +1557,12 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.CONFLICT, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
expected_msg = ("Vnf instance %s in instantiation_state "
|
||||
"NOT_INSTANTIATED. Cannot heal while the vnf instance "
|
||||
"is in this state.")
|
||||
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
|
||||
resp.json['conflictingRequest']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1573,11 +1589,12 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.CONFLICT, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
expected_msg = ("Vnf instance %s in task_state "
|
||||
"HEALING. Cannot heal while the vnf instance "
|
||||
"is in this state.")
|
||||
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
|
||||
resp.json['conflictingRequest']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1609,10 +1626,11 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
self.assertFalse('Location' in resp.headers.keys())
|
||||
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
expected_msg = "Vnfc id %s not present in vnf instance %s"
|
||||
self.assertEqual(expected_msg % (uuidsentinel.vnfc_instance_id,
|
||||
uuidsentinel.vnf_instance_id),
|
||||
resp.json['badRequest']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1735,9 +1753,10 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
|
||||
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("Can not find requested vnf instance: %s" %
|
||||
uuidsentinel.vnf_instance_id,
|
||||
resp.json['itemNotFound']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1751,9 +1770,10 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
|
||||
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual("Can not find requested vnf instance: %s" %
|
||||
constants.INVALID_UUID,
|
||||
resp.json['itemNotFound']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1773,11 +1793,12 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
|
||||
self.assertEqual(http_client.CONFLICT, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
expected_msg = ("Vnf instance %s in instantiation_state "
|
||||
"INSTANTIATED. Cannot delete while the vnf instance "
|
||||
"is in this state.")
|
||||
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
|
||||
resp.json['conflictingRequest']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
@ -1798,11 +1819,12 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
|
||||
self.assertEqual(http_client.CONFLICT, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
expected_msg = ("Vnf instance %s in task_state ERROR. "
|
||||
"Cannot delete while the vnf instance "
|
||||
"is in this state.")
|
||||
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
|
||||
resp.json['conflictingRequest']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||
@ddt.data(
|
||||
@ -3806,9 +3828,10 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
|
||||
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
self.assertEqual(
|
||||
"Can not find requested vnf: %s" % constants.INVALID_UUID,
|
||||
resp.json['itemNotFound']['message'])
|
||||
resp.json['detail'])
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM': FakeVNFMPlugin()})
|
||||
@ -4190,10 +4213,11 @@ class TestController(base.TestCase):
|
||||
resp = req.get_response(self.app)
|
||||
|
||||
self.assertEqual(500, resp.status_code)
|
||||
|
||||
self.assertIn('application/problem+json', resp.headers['Content-Type'])
|
||||
expected_vnf = {'tackerFault': {'code': 500,
|
||||
'message': 'Unexpected API Error. Please report this at '
|
||||
'http://bugs.launchpad.net/tacker/ and attach the '
|
||||
'Tacker API log if possible.\n'
|
||||
"<class 'webob.exc.HTTPInternalServerError'>"}}
|
||||
self.assertEqual(expected_vnf, resp.json)
|
||||
expected_msg = expected_vnf['tackerFault']['message']
|
||||
self.assertEqual(expected_msg, resp.json['detail'])
|
||||
|
@ -19,6 +19,7 @@ Utility methods for working with WSGI servers
|
||||
import functools
|
||||
|
||||
import errno
|
||||
import http.client
|
||||
import os
|
||||
import socket
|
||||
import ssl
|
||||
@ -1038,19 +1039,6 @@ def _default_body_function(wrapped_exc):
|
||||
class Fault(webob.exc.HTTPException):
|
||||
"""Wrap webob.exc.HTTPException to provide API friendly response."""
|
||||
|
||||
_fault_names = {
|
||||
400: "badRequest",
|
||||
401: "unauthorized",
|
||||
403: "forbidden",
|
||||
404: "itemNotFound",
|
||||
405: "badMethod",
|
||||
409: "conflictingRequest",
|
||||
413: "overLimit",
|
||||
415: "badMediaType",
|
||||
429: "overLimit",
|
||||
501: "notImplemented",
|
||||
503: "serviceUnavailable"}
|
||||
|
||||
def __init__(self, exception):
|
||||
"""Create a Fault for the given webob.exc.exception."""
|
||||
self.wrapped_exc = exception
|
||||
@ -1064,22 +1052,24 @@ class Fault(webob.exc.HTTPException):
|
||||
user_locale = req.best_match_language()
|
||||
# Replace the body with fault details.
|
||||
code = self.wrapped_exc.status_int
|
||||
fault_name = self._fault_names.get(code, "tackerFault")
|
||||
fault_name = http.client.responses[code]
|
||||
explanation = self.wrapped_exc.explanation
|
||||
LOG.debug("Returning %(code)s to user: %(explanation)s",
|
||||
{'code': code, 'explanation': explanation})
|
||||
|
||||
explanation = i18n.translate(explanation, user_locale)
|
||||
fault_data = {
|
||||
fault_name: {
|
||||
'code': code,
|
||||
'message': explanation}}
|
||||
fault_data = {}
|
||||
if fault_name is not None:
|
||||
fault_data['title'] = fault_name
|
||||
fault_data['status'] = code
|
||||
fault_data['detail'] = explanation
|
||||
|
||||
if code == 413 or code == 429:
|
||||
retry = self.wrapped_exc.headers.get('Retry-After', None)
|
||||
if retry:
|
||||
fault_data[fault_name]['retryAfter'] = retry
|
||||
fault_data['retryAfter'] = retry
|
||||
|
||||
self.wrapped_exc.content_type = 'application/json'
|
||||
self.wrapped_exc.content_type = 'application/problem+json'
|
||||
self.wrapped_exc.charset = 'UTF-8'
|
||||
|
||||
body = JSONDictSerializer().serialize(fault_data)
|
||||
|
Loading…
Reference in New Issue
Block a user