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:
Yi Feng 2021-06-07 09:51:06 +09:00
parent a98cd4eaa9
commit 646554075e
2 changed files with 59 additions and 45 deletions

View File

@ -645,8 +645,9 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code) 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'.", self.assertEqual("No flavour with id 'invalid'.",
resp.json['badRequest']['message']) resp.json['detail'])
mock_get_vnf.assert_called_once() mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
@ -742,9 +743,10 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code) 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 " self.assertEqual("No instantiation level with id "
"'instantiation_level_1'.", "'instantiation_level_1'.",
resp.json['badRequest']['message']) resp.json['detail'])
mock_get_vnf.assert_called_once() mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
@ -785,8 +787,9 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code) 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'.", self.assertEqual("No instantiation level with id 'non-existing'.",
resp.json['badRequest']['message']) resp.json['detail'])
mock_get_vnf.assert_called_once() mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
@ -887,9 +890,10 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code) 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" % self.assertEqual("VimConnection id is not found: %s" %
uuidsentinel.vim_id, uuidsentinel.vim_id,
resp.json['badRequest']['message']) resp.json['detail'])
mock_get_vnf.assert_called_once() mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
@ -936,9 +940,10 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code) 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" % self.assertEqual("Region not found for the VimConnection: %s" %
uuidsentinel.vim_id, uuidsentinel.vim_id,
resp.json['badRequest']['message']) resp.json['detail'])
mock_get_vnf.assert_called_once() mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
@ -979,8 +984,9 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code) 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.", self.assertEqual("Default VIM is not defined.",
resp.json['badRequest']['message']) resp.json['detail'])
mock_get_vnf.assert_called_once() mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
@ -1032,10 +1038,11 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.CONFLICT, resp.status_code) 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 " expected_msg = ("Vnf instance %s in task_state INSTANTIATING. Cannot "
"instantiate while the vnf instance is in this state.") "instantiate while the vnf instance is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id, self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1093,8 +1100,9 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code) 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", self.assertEqual("'flavourId' is a required property",
resp.json['badRequest']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1134,9 +1142,10 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.NOT_FOUND, resp.status_code) self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertIn('application/problem+json', resp.headers['Content-Type'])
self.assertEqual( self.assertEqual(
"Can not find requested vnf: %s" % constants.INVALID_UUID, "Can not find requested vnf: %s" % constants.INVALID_UUID,
resp.json['itemNotFound']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1159,9 +1168,10 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.NOT_FOUND, resp.status_code) 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" % self.assertEqual("Can not find requested vnf instance: %s" %
uuidsentinel.vnf_instance_id, uuidsentinel.vnf_instance_id,
resp.json['itemNotFound']['message']) resp.json['detail'])
mock_get_vnf.assert_called_once() mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
@ -1261,9 +1271,10 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertEqual(http_client.NOT_FOUND, resp.status_code) 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" % self.assertEqual("Can not find requested vnf instance: %s" %
uuidsentinel.vnf_instance_id, uuidsentinel.vnf_instance_id,
resp.json['itemNotFound']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1275,9 +1286,10 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertEqual(http_client.NOT_FOUND, resp.status_code) 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" % self.assertEqual("Can not find requested vnf instance: %s" %
constants.INVALID_UUID, constants.INVALID_UUID,
resp.json['itemNotFound']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1385,8 +1397,9 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code) 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", self.assertEqual("'terminationType' is a required property",
resp.json['badRequest']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1426,9 +1439,10 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.NOT_FOUND, resp.status_code) 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" % self.assertEqual("Can not find requested vnf instance: %s" %
uuidsentinel.vnf_instance_id, uuidsentinel.vnf_instance_id,
resp.json['itemNotFound']['message']) resp.json['detail'])
mock_get_vnf.assert_called_once() mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
@ -1457,10 +1471,11 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.CONFLICT, resp.status_code) 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 " expected_msg = ("Vnf instance %s in task_state TERMINATING. Cannot "
"terminate while the vnf instance is in this state.") "terminate while the vnf instance is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id, self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message']) resp.json['detail'])
mock_get_vnf.assert_called_once() mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
@ -1542,11 +1557,12 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.CONFLICT, resp.status_code) 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 " expected_msg = ("Vnf instance %s in instantiation_state "
"NOT_INSTANTIATED. Cannot heal while the vnf instance " "NOT_INSTANTIATED. Cannot heal while the vnf instance "
"is in this state.") "is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id, self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1573,11 +1589,12 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.CONFLICT, resp.status_code) 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 " expected_msg = ("Vnf instance %s in task_state "
"HEALING. Cannot heal while the vnf instance " "HEALING. Cannot heal while the vnf instance "
"is in this state.") "is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id, self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1609,10 +1626,11 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys()) self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code) 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" expected_msg = "Vnfc id %s not present in vnf instance %s"
self.assertEqual(expected_msg % (uuidsentinel.vnfc_instance_id, self.assertEqual(expected_msg % (uuidsentinel.vnfc_instance_id,
uuidsentinel.vnf_instance_id), uuidsentinel.vnf_instance_id),
resp.json['badRequest']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1735,9 +1753,10 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertEqual(http_client.NOT_FOUND, resp.status_code) 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" % self.assertEqual("Can not find requested vnf instance: %s" %
uuidsentinel.vnf_instance_id, uuidsentinel.vnf_instance_id,
resp.json['itemNotFound']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1751,9 +1770,10 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertEqual(http_client.NOT_FOUND, resp.status_code) 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" % self.assertEqual("Can not find requested vnf instance: %s" %
constants.INVALID_UUID, constants.INVALID_UUID,
resp.json['itemNotFound']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1773,11 +1793,12 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertEqual(http_client.CONFLICT, resp.status_code) 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 " expected_msg = ("Vnf instance %s in instantiation_state "
"INSTANTIATED. Cannot delete while the vnf instance " "INSTANTIATED. Cannot delete while the vnf instance "
"is in this state.") "is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id, self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -1798,11 +1819,12 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertEqual(http_client.CONFLICT, resp.status_code) 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. " expected_msg = ("Vnf instance %s in task_state ERROR. "
"Cannot delete while the vnf instance " "Cannot delete while the vnf instance "
"is in this state.") "is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id, self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message']) resp.json['detail'])
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_filters")
@ddt.data( @ddt.data(
@ -3806,9 +3828,10 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertEqual(http_client.NOT_FOUND, resp.status_code) self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertIn('application/problem+json', resp.headers['Content-Type'])
self.assertEqual( self.assertEqual(
"Can not find requested vnf: %s" % constants.INVALID_UUID, "Can not find requested vnf: %s" % constants.INVALID_UUID,
resp.json['itemNotFound']['message']) resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()}) return_value={'VNFM': FakeVNFMPlugin()})
@ -4190,10 +4213,11 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertEqual(500, resp.status_code) self.assertEqual(500, resp.status_code)
self.assertIn('application/problem+json', resp.headers['Content-Type'])
expected_vnf = {'tackerFault': {'code': 500, expected_vnf = {'tackerFault': {'code': 500,
'message': 'Unexpected API Error. Please report this at ' 'message': 'Unexpected API Error. Please report this at '
'http://bugs.launchpad.net/tacker/ and attach the ' 'http://bugs.launchpad.net/tacker/ and attach the '
'Tacker API log if possible.\n' 'Tacker API log if possible.\n'
"<class 'webob.exc.HTTPInternalServerError'>"}} "<class 'webob.exc.HTTPInternalServerError'>"}}
self.assertEqual(expected_vnf, resp.json) expected_msg = expected_vnf['tackerFault']['message']
self.assertEqual(expected_msg, resp.json['detail'])

View File

@ -19,6 +19,7 @@ Utility methods for working with WSGI servers
import functools import functools
import errno import errno
import http.client
import os import os
import socket import socket
import ssl import ssl
@ -1038,19 +1039,6 @@ def _default_body_function(wrapped_exc):
class Fault(webob.exc.HTTPException): class Fault(webob.exc.HTTPException):
"""Wrap webob.exc.HTTPException to provide API friendly response.""" """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): def __init__(self, exception):
"""Create a Fault for the given webob.exc.exception.""" """Create a Fault for the given webob.exc.exception."""
self.wrapped_exc = exception self.wrapped_exc = exception
@ -1064,22 +1052,24 @@ class Fault(webob.exc.HTTPException):
user_locale = req.best_match_language() user_locale = req.best_match_language()
# Replace the body with fault details. # Replace the body with fault details.
code = self.wrapped_exc.status_int 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 explanation = self.wrapped_exc.explanation
LOG.debug("Returning %(code)s to user: %(explanation)s", LOG.debug("Returning %(code)s to user: %(explanation)s",
{'code': code, 'explanation': explanation}) {'code': code, 'explanation': explanation})
explanation = i18n.translate(explanation, user_locale) explanation = i18n.translate(explanation, user_locale)
fault_data = { fault_data = {}
fault_name: { if fault_name is not None:
'code': code, fault_data['title'] = fault_name
'message': explanation}} fault_data['status'] = code
fault_data['detail'] = explanation
if code == 413 or code == 429: if code == 413 or code == 429:
retry = self.wrapped_exc.headers.get('Retry-After', None) retry = self.wrapped_exc.headers.get('Retry-After', None)
if retry: 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' self.wrapped_exc.charset = 'UTF-8'
body = JSONDictSerializer().serialize(fault_data) body = JSONDictSerializer().serialize(fault_data)