Remove XML support

XML support in Neutron has always been a second class feature to the
JSON API and broken for many extensions and outputs. The XML API been marked as
deprecated for the Icehouse and Juno release and is ready for removal in
Kilo.

Change-Id: I611aa9382ba3bfb08b2970c63e83b0fdd3d2e8a4
Closes-Bug: #1380787
This commit is contained in:
Mark McClain 2014-10-13 20:38:43 +00:00
parent 9d84e24a85
commit 4209cc4ad6
40 changed files with 43 additions and 1068 deletions

View File

@ -25,7 +25,6 @@ import six
import webob.dec import webob.dec
import webob.exc import webob.exc
from neutron.api.v2 import attributes
from neutron.common import exceptions from neutron.common import exceptions
import neutron.extensions import neutron.extensions
from neutron import manager from neutron import manager
@ -479,19 +478,9 @@ class ExtensionManager(object):
attr_map[resource].update(resource_attrs) attr_map[resource].update(resource_attrs)
else: else:
attr_map[resource] = resource_attrs attr_map[resource] = resource_attrs
if extended_attrs:
attributes.EXT_NSES[ext.get_alias()] = (
ext.get_namespace())
except AttributeError: except AttributeError:
LOG.exception(_("Error fetching extended attributes for " LOG.exception(_("Error fetching extended attributes for "
"extension '%s'"), ext.get_name()) "extension '%s'"), ext.get_name())
try:
comp_map = ext.get_alias_namespace_compatibility_map()
attributes.EXT_NSES_BC.update(comp_map)
except AttributeError:
LOG.info(_("Extension '%s' provides no backward "
"compatibility map for extended attributes"),
ext.get_name())
processed_exts.add(ext_name) processed_exts.add(ext_name)
del exts_to_process[ext_name] del exts_to_process[ext_name]
if len(processed_exts) == processed_ext_count: if len(processed_exts) == processed_ext_count:

View File

@ -762,16 +762,3 @@ PLURALS = {NETWORKS: NETWORK,
'allocation_pools': 'allocation_pool', 'allocation_pools': 'allocation_pool',
'fixed_ips': 'fixed_ip', 'fixed_ips': 'fixed_ip',
'extensions': 'extension'} 'extensions': 'extension'}
EXT_NSES = {}
# Namespaces to be added for backward compatibility
# when existing extended resource attributes are
# provided by other extension than original one.
EXT_NSES_BC = {}
def get_attr_metadata():
return {'plurals': PLURALS,
'xmlns': constants.XML_NS_V20,
constants.EXT_NS: EXT_NSES,
constants.EXT_NS_COMP: EXT_NSES_BC}

View File

@ -24,7 +24,6 @@ import six
import webob.dec import webob.dec
import webob.exc import webob.exc
from neutron.api.v2 import attributes
from neutron.common import exceptions from neutron.common import exceptions
from neutron.openstack.common import gettextutils from neutron.openstack.common import gettextutils
from neutron.openstack.common import log as logging from neutron.openstack.common import log as logging
@ -42,14 +41,9 @@ def Resource(controller, faults=None, deserializers=None, serializers=None):
"""Represents an API entity resource and the associated serialization and """Represents an API entity resource and the associated serialization and
deserialization logic deserialization logic
""" """
xml_deserializer = wsgi.XMLDeserializer(attributes.get_attr_metadata()) default_deserializers = {'application/json': wsgi.JSONDeserializer()}
default_deserializers = {'application/xml': xml_deserializer, default_serializers = {'application/json': wsgi.JSONDictSerializer()}
'application/json': wsgi.JSONDeserializer()} format_types = {'json': 'application/json'}
xml_serializer = wsgi.XMLDictSerializer(attributes.get_attr_metadata())
default_serializers = {'application/xml': xml_serializer,
'application/json': wsgi.JSONDictSerializer()}
format_types = {'xml': 'application/xml',
'json': 'application/json'}
action_status = dict(create=201, delete=204) action_status = dict(create=201, delete=204)
default_deserializers.update(deserializers or {}) default_deserializers.update(deserializers or {})

View File

@ -36,7 +36,7 @@ RESOURCES = {'network': 'networks',
SUB_RESOURCES = {} SUB_RESOURCES = {}
COLLECTION_ACTIONS = ['index', 'create'] COLLECTION_ACTIONS = ['index', 'create']
MEMBER_ACTIONS = ['show', 'update', 'delete'] MEMBER_ACTIONS = ['show', 'update', 'delete']
REQUIREMENTS = {'id': attributes.UUID_PATTERN, 'format': 'xml|json'} REQUIREMENTS = {'id': attributes.UUID_PATTERN, 'format': 'json'}
class Index(wsgi.Application): class Index(wsgi.Application):
@ -45,9 +45,7 @@ class Index(wsgi.Application):
@webob.dec.wsgify(RequestClass=wsgi.Request) @webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req): def __call__(self, req):
metadata = {'application/xml': {'attributes': { metadata = {}
'resource': ['name', 'collection'],
'link': ['href', 'rel']}}}
layout = [] layout = []
for name, collection in self.resources.iteritems(): for name, collection in self.resources.iteritems():

View File

@ -49,14 +49,7 @@ class Versions(object):
builder = versions_view.get_view_builder(req) builder = versions_view.get_view_builder(req)
versions = [builder.build(version) for version in version_objs] versions = [builder.build(version) for version in version_objs]
response = dict(versions=versions) response = dict(versions=versions)
metadata = { metadata = {}
"application/xml": {
"attributes": {
"version": ["status", "id"],
"link": ["rel", "href"],
}
}
}
content_type = req.best_match_content_type() content_type = req.best_match_content_type()
body = (wsgi.Serializer(metadata=metadata). body = (wsgi.Serializer(metadata=metadata).

View File

@ -72,19 +72,6 @@ MAX_VXLAN_VNI = 2 ** 24 - 1
FLOODING_ENTRY = ['00:00:00:00:00:00', '0.0.0.0'] FLOODING_ENTRY = ['00:00:00:00:00:00', '0.0.0.0']
EXT_NS_COMP = '_backward_comp_e_ns'
EXT_NS = '_extension_ns'
XML_NS_V20 = 'http://openstack.org/quantum/api/v2.0'
XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance"
XSI_ATTR = "xsi:nil"
XSI_NIL_ATTR = "xmlns:xsi"
ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"
ATOM_XMLNS = "xmlns:atom"
ATOM_LINK_NOTATION = "{%s}link" % ATOM_NAMESPACE
TYPE_XMLNS = "xmlns:quantum"
TYPE_ATTR = "quantum:type"
VIRTUAL_ROOT_KEY = "_v_root"
TYPE_BOOL = "bool" TYPE_BOOL = "bool"
TYPE_INT = "int" TYPE_INT = "int"
TYPE_LONG = "long" TYPE_LONG = "long"

View File

@ -203,10 +203,6 @@ class L3RouterApplianceVMTestCase(
'neutron.db.l3_db.L3_NAT_dbonly_mixin._check_and_get_fip_assoc') 'neutron.db.l3_db.L3_NAT_dbonly_mixin._check_and_get_fip_assoc')
class L3RouterApplianceVMTestCaseXML(L3RouterApplianceVMTestCase):
fmt = 'xml'
class CfgAgentRouterApplianceVMTestCase(L3RouterApplianceTestCaseBase, class CfgAgentRouterApplianceVMTestCase(L3RouterApplianceTestCaseBase,
test_l3_plugin.L3AgentDbTestCaseBase): test_l3_plugin.L3AgentDbTestCaseBase):

View File

@ -1237,7 +1237,3 @@ class TestFirewallDBPlugin(FirewallPluginDbTestCase):
expected_code=webob.exc.HTTPNotFound.code, expected_code=webob.exc.HTTPNotFound.code,
expected_body=None, expected_body=None,
body_data={'firewall_rule_id': None}) body_data={'firewall_rule_id': None})
class TestFirewallDBPluginXML(TestFirewallDBPlugin):
fmt = 'xml'

View File

@ -1584,7 +1584,3 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
SystemExit, SystemExit,
loadbalancer_plugin.LoadBalancerPlugin loadbalancer_plugin.LoadBalancerPlugin
) )
class TestLoadBalancerXML(TestLoadBalancer):
fmt = 'xml'

View File

@ -294,7 +294,3 @@ class TestMetering(MeteringPluginDbTestCase):
self._test_list_resources('metering-label-rule', self._test_list_resources('metering-label-rule',
metering_label_rule) metering_label_rule)
class TestMeteringDbXML(TestMetering):
fmt = 'xml'

View File

@ -1596,7 +1596,3 @@ class TestVpnaas(VPNPluginDbTestCase):
) )
delete_res = delete_req.get_response(self.ext_api) delete_res = delete_req.get_response(self.ext_api)
self.assertEqual(409, delete_res.status_int) self.assertEqual(409, delete_res.status_int)
class TestVpnaasXML(TestVpnaas):
fmt = 'xml'

View File

@ -752,15 +752,3 @@ class TestCiscoSubnetsV2(CiscoML2MechanismTestCase,
res, res,
'subnets', 'subnets',
wexc.HTTPInternalServerError.code) wexc.HTTPInternalServerError.code)
class TestCiscoPortsV2XML(TestCiscoPortsV2):
fmt = 'xml'
class TestCiscoNetworksV2XML(TestCiscoNetworksV2):
fmt = 'xml'
class TestCiscoSubnetsV2XML(TestCiscoSubnetsV2):
fmt = 'xml'

View File

@ -89,17 +89,7 @@ class TestMl2SecurityGroups(Ml2SecurityGroupsTestCase,
self.assertIsNone(port_dict) self.assertIsNone(port_dict)
class TestMl2SecurityGroupsXML(TestMl2SecurityGroups):
fmt = 'xml'
class TestMl2SGServerRpcCallBack( class TestMl2SGServerRpcCallBack(
Ml2SecurityGroupsTestCase, Ml2SecurityGroupsTestCase,
test_sg_rpc.SGServerRpcCallBackTestCase): test_sg_rpc.SGServerRpcCallBackTestCase):
pass pass
class TestMl2SGServerRpcCallBackXML(
Ml2SecurityGroupsTestCase,
test_sg_rpc.SGServerRpcCallBackTestCaseXML):
pass

View File

@ -56,10 +56,6 @@ class TestMlnxSecurityGroups(MlnxSecurityGroupsTestCase,
pass pass
class TestMlnxSecurityGroupsXML(TestMlnxSecurityGroups):
fmt = 'xml'
class TestMlnxSecurityGroupsDB(MlnxSecurityGroupsTestCase): class TestMlnxSecurityGroupsDB(MlnxSecurityGroupsTestCase):
def test_security_group_get_port_from_device(self): def test_security_group_get_port_from_device(self):
with self.network() as n: with self.network() as n:
@ -94,7 +90,3 @@ class TestMlnxSecurityGroupsDB(MlnxSecurityGroupsTestCase):
def test_security_group_get_port_from_device_with_no_port(self): def test_security_group_get_port_from_device_with_no_port(self):
port_dict = mlnx_db.get_port_from_device('bad_device_id') port_dict = mlnx_db.get_port_from_device('bad_device_id')
self.assertIsNone(port_dict) self.assertIsNone(port_dict)
class TestMlnxSecurityGroupsDBXML(TestMlnxSecurityGroupsDB):
fmt = 'xml'

View File

@ -56,12 +56,6 @@ class TestNecSGServerRpcCallBack(
pass pass
class TestNecSGServerRpcCallBackXML(
test_sg_rpc.SGServerRpcCallBackTestCaseXML,
NecSecurityGroupsTestCase):
pass
class TestNecSecurityGroups(NecSecurityGroupsTestCase, class TestNecSecurityGroups(NecSecurityGroupsTestCase,
test_sg.TestSecurityGroups, test_sg.TestSecurityGroups,
test_sg_rpc.SGNotificationTestMixin): test_sg_rpc.SGNotificationTestMixin):
@ -92,7 +86,3 @@ class TestNecSecurityGroups(NecSecurityGroupsTestCase,
self.assertEqual([fixed_ips[0]['ip_address']], self.assertEqual([fixed_ips[0]['ip_address']],
port_dict['fixed_ips']) port_dict['fixed_ips'])
self._delete('ports', port_id) self._delete('ports', port_id)
class TestNecSecurityGroupsXML(TestNecSecurityGroups):
fmt = 'xml'

View File

@ -70,12 +70,6 @@ class TestOneConvergenceSGServerRpcCallBack(
pass pass
class TestOneConvergenceSGServerRpcCallBackXML(
OneConvergenceSecurityGroupsTestCase,
test_sg_rpc.SGServerRpcCallBackTestCaseXML):
pass
class TestOneConvergenceSecurityGroups(OneConvergenceSecurityGroupsTestCase, class TestOneConvergenceSecurityGroups(OneConvergenceSecurityGroupsTestCase,
test_sg.TestSecurityGroups, test_sg.TestSecurityGroups,
test_sg_rpc.SGNotificationTestMixin): test_sg_rpc.SGNotificationTestMixin):
@ -113,7 +107,3 @@ class TestOneConvergenceSecurityGroups(OneConvergenceSecurityGroupsTestCase,
plugin = manager.NeutronManager.get_plugin() plugin = manager.NeutronManager.get_plugin()
port_dict = plugin.get_port_from_device('bad_device_id') port_dict = plugin.get_port_from_device('bad_device_id')
self.assertIsNone(port_dict) self.assertIsNone(port_dict)
class TestOneConvergenceSecurityGroupsXML(TestOneConvergenceSecurityGroups):
fmt = 'xml'

View File

@ -1413,7 +1413,3 @@ class OvsL3AgentNotifierTestCase(test_l3_plugin.L3NatTestCaseMixin,
mock.ANY, l3_notifier.make_msg( mock.ANY, l3_notifier.make_msg(
'agent_updated', payload={'admin_state_up': False}), 'agent_updated', payload={'admin_state_up': False}),
topic='l3_agent.hosta') topic='l3_agent.hosta')
class OvsAgentSchedulerTestCaseXML(OvsAgentSchedulerTestCase):
fmt = 'xml'

View File

@ -60,8 +60,3 @@ class SecurityGroupsTestCase(ext_sg.SecurityGroupDBTestCase):
class TestSecurityGroups(ext_sg.TestSecurityGroups, SecurityGroupsTestCase): class TestSecurityGroups(ext_sg.TestSecurityGroups, SecurityGroupsTestCase):
pass pass
class TestSecurityGroupsXML(TestSecurityGroups):
fmt = 'xml'

View File

@ -84,7 +84,3 @@ class TestRyuSecurityGroups(RyuSecurityGroupsTestCase,
plugin = manager.NeutronManager.get_plugin() plugin = manager.NeutronManager.get_plugin()
port_dict = plugin.get_port_from_device('bad_device_id') port_dict = plugin.get_port_from_device('bad_device_id')
self.assertIsNone(port_dict) self.assertIsNone(port_dict)
class TestRyuSecurityGroupsXML(TestRyuSecurityGroups):
fmt = 'xml'

View File

@ -215,7 +215,3 @@ class LBaaSAgentSchedulerTestCase(test_agent_ext_plugin.AgentDBTestMixIn,
'fake_id', 'fake_id',
expected_code=exc.HTTPForbidden.code, expected_code=exc.HTTPForbidden.code,
admin_context=False) admin_context=False)
class LBaaSAgentSchedulerTestCaseXML(LBaaSAgentSchedulerTestCase):
fmt = 'xml'

View File

@ -456,7 +456,3 @@ class LoadBalancerExtensionTestCase(test_api_v2_extension.ExtensionTestCase):
instance.delete_pool_health_monitor.assert_called_with( instance.delete_pool_health_monitor.assert_called_with(
mock.ANY, health_monitor_id, pool_id='id1') mock.ANY, health_monitor_id, pool_id='id1')
self.assertEqual(res.status_int, exc.HTTPNoContent.code) self.assertEqual(res.status_int, exc.HTTPNoContent.code)
class LoadBalancerExtensionTestCaseXML(LoadBalancerExtensionTestCase):
fmt = 'xml'

View File

@ -127,10 +127,6 @@ class LBaaSQuotaExtensionDbTestCase(LBaaSQuotaExtensionTestCase):
self.assertEqual(-1, quota['quota']['health_monitor']) self.assertEqual(-1, quota['quota']['health_monitor'])
class LBaaSQuotaExtensionDbTestCaseXML(LBaaSQuotaExtensionDbTestCase):
fmt = 'xml'
class LBaaSQuotaExtensionCfgTestCase( class LBaaSQuotaExtensionCfgTestCase(
LBaaSQuotaExtensionTestCase): LBaaSQuotaExtensionTestCase):
@ -160,7 +156,3 @@ class LBaaSQuotaExtensionCfgTestCase(
self.serialize(quotas), self.serialize(quotas),
expect_errors=True) expect_errors=True)
self.assertEqual(403, res.status_int) self.assertEqual(403, res.status_int)
class LBaaSQuotaExtensionCfgTestCaseXML(LBaaSQuotaExtensionCfgTestCase):
fmt = 'xml'

View File

@ -520,7 +520,3 @@ class VpnaasExtensionTestCase(test_api_v2_extension.ExtensionTestCase):
def test_ipsec_site_connection_delete(self): def test_ipsec_site_connection_delete(self):
"""Test case to delete a ipsec_site_connection.""" """Test case to delete a ipsec_site_connection."""
self._test_entity_delete('ipsec_site_connection') self._test_entity_delete('ipsec_site_connection')
class VpnaasExtensionTestCaseXML(VpnaasExtensionTestCase):
fmt = 'xml'

View File

@ -251,7 +251,3 @@ class AgentDBTestCase(AgentDBTestMixIn,
agents = self._list_agents( agents = self._list_agents(
query_string='binary=neutron-l3-agent&host=' + L3_HOSTB) query_string='binary=neutron-l3-agent&host=' + L3_HOSTB)
self.assertFalse(agents['agents'][0]['alive']) self.assertFalse(agents['agents'][0]['alive'])
class AgentDBTestCaseXML(AgentDBTestCase):
fmt = 'xml'

View File

@ -1196,10 +1196,6 @@ class SubresourceTest(base.BaseTestCase, testlib_plugin.PluginSetupHelper):
# Note: since all resources use the same controller and validation # Note: since all resources use the same controller and validation
# logic, we actually get really good coverage from testing just networks. # logic, we actually get really good coverage from testing just networks.
class XMLV2TestCase(JSONV2TestCase):
fmt = 'xml'
class V2Views(base.BaseTestCase): class V2Views(base.BaseTestCase):
def _view(self, keys, collection, resource): def _view(self, keys, collection, resource):
data = dict((key, 'value') for key in keys) data = dict((key, 'value') for key in keys)

View File

@ -42,8 +42,7 @@ class RequestTestCase(base.BaseTestCase):
self.assertEqual(result, "application/json") self.assertEqual(result, "application/json")
def test_content_type_from_accept(self): def test_content_type_from_accept(self):
for content_type in ('application/xml', content_type = 'application/json'
'application/json'):
request = wsgi.Request.blank('/tests/123') request = wsgi.Request.blank('/tests/123')
request.headers["Accept"] = content_type request.headers["Accept"] = content_type
result = request.best_match_content_type() result = request.best_match_content_type()
@ -51,7 +50,7 @@ class RequestTestCase(base.BaseTestCase):
def test_content_type_from_accept_best(self): def test_content_type_from_accept_best(self):
request = wsgi.Request.blank('/tests/123') request = wsgi.Request.blank('/tests/123')
request.headers["Accept"] = "application/xml, application/json" request.headers["Accept"] = "application/json"
result = request.best_match_content_type() result = request.best_match_content_type()
self.assertEqual(result, "application/json") self.assertEqual(result, "application/json")
@ -59,13 +58,9 @@ class RequestTestCase(base.BaseTestCase):
request.headers["Accept"] = ("application/json; q=0.3, " request.headers["Accept"] = ("application/json; q=0.3, "
"application/xml; q=0.9") "application/xml; q=0.9")
result = request.best_match_content_type() result = request.best_match_content_type()
self.assertEqual(result, "application/xml") self.assertEqual(result, "application/json")
def test_content_type_from_query_extension(self): def test_content_type_from_query_extension(self):
request = wsgi.Request.blank('/tests/123.xml')
result = request.best_match_content_type()
self.assertEqual(result, "application/xml")
request = wsgi.Request.blank('/tests/123.json') request = wsgi.Request.blank('/tests/123.json')
result = request.best_match_content_type() result = request.best_match_content_type()
self.assertEqual(result, "application/json") self.assertEqual(result, "application/json")
@ -75,10 +70,10 @@ class RequestTestCase(base.BaseTestCase):
self.assertEqual(result, "application/json") self.assertEqual(result, "application/json")
def test_content_type_accept_and_query_extension(self): def test_content_type_accept_and_query_extension(self):
request = wsgi.Request.blank('/tests/123.xml') request = wsgi.Request.blank('/tests/123.json')
request.headers["Accept"] = "application/json" request.headers["Accept"] = "application/xml"
result = request.best_match_content_type() result = request.best_match_content_type()
self.assertEqual(result, "application/xml") self.assertEqual(result, "application/json")
def test_content_type_accept_default(self): def test_content_type_accept_default(self):
request = wsgi.Request.blank('/tests/123.unsupported') request = wsgi.Request.blank('/tests/123.unsupported')
@ -121,10 +116,7 @@ class ResourceTestCase(base.BaseTestCase):
@staticmethod @staticmethod
def _get_deserializer(req_format): def _get_deserializer(req_format):
if req_format == 'json':
return wsgi.JSONDeserializer() 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'
@ -148,28 +140,6 @@ class ResourceTestCase(base.BaseTestCase):
self.assertEqual(wsgi.JSONDeserializer().deserialize(res.body), self.assertEqual(wsgi.JSONDeserializer().deserialize(res.body),
expected_res) expected_res)
def test_unmapped_neutron_error_with_xml(self):
msg = u'\u7f51\u7edc'
class TestException(n_exc.NeutronException):
message = msg
expected_res = {'body': {
'NeutronError': {
'type': 'TestException',
'message': msg,
'detail': ''}}}
controller = mock.MagicMock()
controller.test.side_effect = TestException()
resource = webtest.TestApp(wsgi_resource.Resource(controller))
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)
@mock.patch('neutron.openstack.common.gettextutils.translate') @mock.patch('neutron.openstack.common.gettextutils.translate')
def test_unmapped_neutron_error_localized(self, mock_translation): def test_unmapped_neutron_error_localized(self, mock_translation):
gettextutils.install('blaa', lazy=True) gettextutils.install('blaa', lazy=True)
@ -216,30 +186,6 @@ class ResourceTestCase(base.BaseTestCase):
self.assertEqual(wsgi.JSONDeserializer().deserialize(res.body), self.assertEqual(wsgi.JSONDeserializer().deserialize(res.body),
expected_res) expected_res)
def test_mapped_neutron_error_with_xml(self):
msg = u'\u7f51\u7edc'
class TestException(n_exc.NeutronException):
message = msg
expected_res = {'body': {
'NeutronError': {
'type': 'TestException',
'message': msg,
'detail': ''}}}
controller = mock.MagicMock()
controller.test.side_effect = TestException()
faults = {TestException: exc.HTTPGatewayTimeout}
resource = webtest.TestApp(wsgi_resource.Resource(controller,
faults=faults))
environ = {'wsgiorg.routing_args': (None, {'action': 'test',
'format': 'xml'})}
res = resource.get('', extra_environ=environ, expect_errors=True)
self.assertEqual(res.status_int, exc.HTTPGatewayTimeout.code)
self.assertEqual(wsgi.XMLDeserializer().deserialize(res.body),
expected_res)
@mock.patch('neutron.openstack.common.gettextutils.translate') @mock.patch('neutron.openstack.common.gettextutils.translate')
def test_mapped_neutron_error_localized(self, mock_translation): def test_mapped_neutron_error_localized(self, mock_translation):
gettextutils.install('blaa', lazy=True) gettextutils.install('blaa', lazy=True)
@ -308,9 +254,6 @@ class ResourceTestCase(base.BaseTestCase):
def test_unhandled_error_with_json(self): def test_unhandled_error_with_json(self):
self._test_unhandled_error() 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'): def _test_not_implemented_error(self, req_format='json'):
expected_res = {'body': {'NeutronError': expected_res = {'body': {'NeutronError':
{'detail': '', {'detail': '',
@ -330,9 +273,6 @@ class ResourceTestCase(base.BaseTestCase):
def test_not_implemented_error_with_json(self): def test_not_implemented_error_with_json(self):
self._test_not_implemented_error() 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): def test_status_200(self):
controller = mock.MagicMock() controller = mock.MagicMock()
controller.test = lambda request: {'foo': 'bar'} controller.test = lambda request: {'foo': 'bar'}

View File

@ -4061,23 +4061,3 @@ class NeutronDbPluginV2AsMixinTestCase(testlib_api.SqlTestCase):
self.net_data['network']['status'] = 'BUILD' self.net_data['network']['status'] = 'BUILD'
net = self.plugin.create_network(self.context, self.net_data) net = self.plugin.create_network(self.context, self.net_data)
self.assertEqual(net['status'], 'BUILD') self.assertEqual(net['status'], 'BUILD')
class TestBasicGetXML(TestBasicGet):
fmt = 'xml'
class TestNetworksV2XML(TestNetworksV2):
fmt = 'xml'
class TestPortsV2XML(TestPortsV2):
fmt = 'xml'
class TestSubnetsV2XML(TestSubnetsV2):
fmt = 'xml'
class TestV2HTTPResponseXML(TestV2HTTPResponse):
fmt = 'xml'

View File

@ -282,7 +282,3 @@ class TestAllowedAddressPairs(AllowedAddressPairDBTestCase):
port = self.deserialize(self.fmt, req.get_response(self.api)) port = self.deserialize(self.fmt, req.get_response(self.api))
self.assertEqual(port['port'][addr_pair.ADDRESS_PAIRS], []) self.assertEqual(port['port'][addr_pair.ADDRESS_PAIRS], [])
self._delete('ports', port['port']['id']) self._delete('ports', port['port']['id'])
class TestAllowedAddressPairsXML(TestAllowedAddressPairs):
fmt = 'xml'

View File

@ -180,7 +180,3 @@ class ExtNetDBTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
self.assertEqual(res.status_int, exc.HTTPNoContent.code) self.assertEqual(res.status_int, exc.HTTPNoContent.code)
(l3_mock.delete_disassociated_floatingips (l3_mock.delete_disassociated_floatingips
.assert_called_once_with(mock.ANY, net['network']['id'])) .assert_called_once_with(mock.ANY, net['network']['id']))
class ExtNetDBTestCaseXML(ExtNetDBTestCase):
fmt = 'xml'

View File

@ -469,10 +469,6 @@ class ExtraRouteDBIntTestCase(test_l3.L3NatDBIntTestCase,
self.setup_notification_driver() self.setup_notification_driver()
class ExtraRouteDBIntTestCaseXML(ExtraRouteDBIntTestCase):
fmt = 'xml'
class ExtraRouteDBSepTestCase(test_l3.L3NatDBSepTestCase, class ExtraRouteDBSepTestCase(test_l3.L3NatDBSepTestCase,
ExtraRouteDBTestCaseBase): ExtraRouteDBTestCaseBase):
def setUp(self): def setUp(self):
@ -492,7 +488,3 @@ class ExtraRouteDBSepTestCase(test_l3.L3NatDBSepTestCase,
service_plugins=service_plugins) service_plugins=service_plugins)
self.setup_notification_driver() self.setup_notification_driver()
class ExtraRouteDBSepTestCaseXML(ExtraRouteDBSepTestCase):
fmt = 'xml'

View File

@ -372,10 +372,6 @@ class FirewallExtensionTestCase(test_api_v2_extension.ExtensionTestCase):
self.assertEqual(res, return_value) self.assertEqual(res, return_value)
class FirewallExtensionTestCaseXML(FirewallExtensionTestCase):
fmt = 'xml'
class TestFirewallAttributeValidators(base.BaseTestCase): class TestFirewallAttributeValidators(base.BaseTestCase):
def test_validate_port_range(self): def test_validate_port_range(self):

View File

@ -168,10 +168,6 @@ class SecurityGroupsTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
self.assertEqual(security_group_rule[k], v) self.assertEqual(security_group_rule[k], v)
class SecurityGroupsTestCaseXML(SecurityGroupsTestCase):
fmt = 'xml'
class SecurityGroupTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, class SecurityGroupTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
securitygroups_db.SecurityGroupDbMixin): securitygroups_db.SecurityGroupDbMixin):
"""Test plugin that implements necessary calls on create/delete port for """Test plugin that implements necessary calls on create/delete port for
@ -1445,7 +1441,3 @@ class TestConvertProtocol(base.BaseTestCase):
for val in ['bad', '256', '-1']: for val in ['bad', '256', '-1']:
self.assertRaises(ext_sg.SecurityGroupRuleInvalidProtocol, self.assertRaises(ext_sg.SecurityGroupRuleInvalidProtocol,
ext_sg.convert_protocol, val) ext_sg.convert_protocol, val)
class TestSecurityGroupsXML(TestSecurityGroups):
fmt = 'xml'

View File

@ -627,10 +627,6 @@ class ExtensionControllerTest(testlib_api.WebTestCase):
self.assertEqual(response.status_int, 404) self.assertEqual(response.status_int, 404)
class ExtensionControllerTestXML(ExtensionControllerTest):
fmt = 'xml'
def app_factory(global_conf, **local_conf): def app_factory(global_conf, **local_conf):
conf = global_conf.copy() conf = global_conf.copy()
conf.update(local_conf) conf.update(local_conf)

View File

@ -214,10 +214,6 @@ class L3NatExtensionTestCase(test_api_v2_extension.ExtensionTestCase):
self.assertEqual(res['subnet_id'], subnet_id) self.assertEqual(res['subnet_id'], subnet_id)
class L3NatExtensionTestCaseXML(L3NatExtensionTestCase):
fmt = 'xml'
# This base plugin class is for tests. # This base plugin class is for tests.
class TestL3NatBasePlugin(db_base_plugin_v2.NeutronDbPluginV2, class TestL3NatBasePlugin(db_base_plugin_v2.NeutronDbPluginV2,
external_net_db.External_net_db_mixin): external_net_db.External_net_db_mixin):
@ -2144,11 +2140,3 @@ class L3NatDBSepTestCase(L3BaseForSepTests, L3NatTestCaseBase):
"""Unit tests for a separate L3 routing service plugin.""" """Unit tests for a separate L3 routing service plugin."""
pass pass
class L3NatDBIntTestCaseXML(L3NatDBIntTestCase):
fmt = 'xml'
class L3NatDBSepTestCaseXML(L3NatDBSepTestCase):
fmt = 'xml'

View File

@ -320,10 +320,6 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
self.assertEqual(400, res.status_int) self.assertEqual(400, res.status_int)
class QuotaExtensionDbTestCaseXML(QuotaExtensionDbTestCase):
fmt = 'xml'
class QuotaExtensionCfgTestCase(QuotaExtensionTestCase): class QuotaExtensionCfgTestCase(QuotaExtensionTestCase):
fmt = 'json' fmt = 'json'
@ -378,10 +374,6 @@ class QuotaExtensionCfgTestCase(QuotaExtensionTestCase):
self.assertEqual(403, res.status_int) self.assertEqual(403, res.status_int)
class QuotaExtensionCfgTestCaseXML(QuotaExtensionCfgTestCase):
fmt = 'xml'
class TestDbQuotaDriver(base.BaseTestCase): class TestDbQuotaDriver(base.BaseTestCase):
"""Test for neutron.db.quota_db.DbQuotaDriver.""" """Test for neutron.db.quota_db.DbQuotaDriver."""

View File

@ -970,10 +970,6 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
self._delete('ports', port_id2) self._delete('ports', port_id2)
class SGServerRpcCallBackTestCaseXML(SGServerRpcCallBackTestCase):
fmt = 'xml'
class SGAgentRpcCallBackMixinTestCase(base.BaseTestCase): class SGAgentRpcCallBackMixinTestCase(base.BaseTestCase):
def setUp(self): def setUp(self):
super(SGAgentRpcCallBackMixinTestCase, self).setUp() super(SGAgentRpcCallBackMixinTestCase, self).setUp()

View File

@ -202,10 +202,6 @@ class ServiceTypeExtensionTestCase(ServiceTypeExtensionTestCaseBase):
self.assertEqual(res.status_int, webexc.HTTPOk.code) self.assertEqual(res.status_int, webexc.HTTPOk.code)
class ServiceTypeExtensionTestCaseXML(ServiceTypeExtensionTestCase):
fmt = 'xml'
class ServiceTypeManagerExtTestCase(ServiceTypeExtensionTestCaseBase): class ServiceTypeManagerExtTestCase(ServiceTypeExtensionTestCaseBase):
"""Tests ServiceTypemanager as a public API.""" """Tests ServiceTypemanager as a public API."""
def setUp(self): def setUp(self):
@ -227,7 +223,3 @@ class ServiceTypeManagerExtTestCase(ServiceTypeExtensionTestCaseBase):
data = self.deserialize(res) data = self.deserialize(res)
self.assertIn('service_providers', data) self.assertIn('service_providers', data)
self.assertEqual(len(data['service_providers']), 2) self.assertEqual(len(data['service_providers']), 2)
class ServiceTypeManagerExtTestCaseXML(ServiceTypeManagerExtTestCase):
fmt = 'xml'

View File

@ -23,8 +23,6 @@ import testtools
import webob import webob
import webob.exc import webob.exc
from neutron.api.v2 import attributes
from neutron.common import constants
from neutron.common import exceptions as exception from neutron.common import exceptions as exception
from neutron.tests import base from neutron.tests import base
from neutron import wsgi from neutron import wsgi
@ -154,33 +152,16 @@ class SerializerTest(base.BaseTestCase):
"""Test serialize with content type json.""" """Test serialize with content type json."""
input_data = {'servers': ['test=pass']} input_data = {'servers': ['test=pass']}
content_type = 'application/json' content_type = 'application/json'
serializer = wsgi.Serializer(default_xmlns="fake") serializer = wsgi.Serializer()
result = serializer.serialize(input_data, content_type) result = serializer.serialize(input_data, content_type)
self.assertEqual('{"servers": ["test=pass"]}', result) self.assertEqual('{"servers": ["test=pass"]}', result)
def test_serialize_content_type_xml(self):
"""Test serialize with content type xml."""
input_data = {'servers': ['test=pass']}
content_type = 'application/xml'
serializer = wsgi.Serializer(default_xmlns="fake")
result = serializer.serialize(input_data, content_type)
expected = (
'<?xml version=\'1.0\''
' encoding=\'UTF-8\'?>\n'
'<servers xmlns="http://openstack.org/quantum/api/v2.0" '
'xmlns:quantum="http://openstack.org/quantum/api/v2.0" '
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
'<server>test=pass</server></servers>'
)
self.assertEqual(expected, result)
def test_deserialize_raise_bad_request(self): def test_deserialize_raise_bad_request(self):
"""Test serialize verifies that exception is raises.""" """Test serialize verifies that exception is raises."""
content_type = 'application/unknown' content_type = 'application/unknown'
data_string = 'test' data_string = 'test'
serializer = wsgi.Serializer(default_xmlns="fake") serializer = wsgi.Serializer()
self.assertRaises( self.assertRaises(
webob.exc.HTTPBadRequest, webob.exc.HTTPBadRequest,
@ -190,100 +171,11 @@ class SerializerTest(base.BaseTestCase):
"""Test Serializer.deserialize with content type json.""" """Test Serializer.deserialize with content type json."""
content_type = 'application/json' content_type = 'application/json'
data_string = '{"servers": ["test=pass"]}' data_string = '{"servers": ["test=pass"]}'
serializer = wsgi.Serializer(default_xmlns="fake") serializer = wsgi.Serializer()
result = serializer.deserialize(data_string, content_type) result = serializer.deserialize(data_string, content_type)
self.assertEqual({'body': {u'servers': [u'test=pass']}}, result) self.assertEqual({'body': {u'servers': [u'test=pass']}}, result)
def test_deserialize_xml_content_type(self):
"""Test deserialize with content type xml."""
content_type = 'application/xml'
data_string = (
'<servers xmlns="fake">'
'<server>test=pass</server>'
'</servers>'
)
serializer = wsgi.Serializer(
default_xmlns="fake", metadata={'xmlns': 'fake'})
result = serializer.deserialize(data_string, content_type)
expected = {'body': {'servers': {'server': 'test=pass'}}}
self.assertEqual(expected, result)
def test_deserialize_xml_content_type_with_meta(self):
"""Test deserialize with content type xml with meta."""
content_type = 'application/xml'
data_string = (
'<servers>'
'<server name="s1">'
'<test test="a">passed</test>'
'</server>'
'</servers>'
)
metadata = {'plurals': {'servers': 'server'}, 'xmlns': 'fake'}
serializer = wsgi.Serializer(
default_xmlns="fake", metadata=metadata)
result = serializer.deserialize(data_string, content_type)
expected = {'body': {'servers': [{'name': 's1', 'test': 'passed'}]}}
self.assertEqual(expected, result)
def test_serialize_xml_root_key_is_dict(self):
"""Test Serializer.serialize with content type xml with meta dict."""
content_type = 'application/xml'
data = {'servers': {'network': (2, 3)}}
metadata = {'xmlns': 'fake'}
serializer = wsgi.Serializer(default_xmlns="fake", metadata=metadata)
result = serializer.serialize(data, content_type)
result = result.replace('\n', '')
expected = (
'<?xml version=\'1.0\' encoding=\'UTF-8\'?>'
'<servers xmlns="fake" xmlns:quantum="fake" '
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
'<network>(2, 3)</network></servers>'
)
self.assertEqual(result, expected)
def test_serialize_xml_root_key_is_list(self):
"""Test serialize with content type xml with meta list."""
input_dict = {'servers': ['test=pass']}
content_type = 'application/xml'
metadata = {'application/xml': {
'xmlns': 'fake'}}
serializer = wsgi.Serializer(default_xmlns="fake", metadata=metadata)
result = serializer.serialize(input_dict, content_type)
result = result.replace('\n', '').replace(' ', '')
expected = (
'<?xmlversion=\'1.0\''
'encoding=\'UTF-8\'?>'
'<serversxmlns="http://openstack.org/quantum/api/v2.0"'
'xmlns:quantum="http://openstack.org/quantum/api/v2.0"'
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
'<server>test=pass</server></servers>'
)
self.assertEqual(result, expected)
def test_serialize_xml_root_is_None(self):
input_dict = {'test': 'pass'}
content_type = 'application/xml'
serializer = wsgi.Serializer(default_xmlns="fake")
result = serializer.serialize(input_dict, content_type)
result = result.replace('\n', '').replace(' ', '')
expected = (
'<?xmlversion=\'1.0\''
'encoding=\'UTF-8\'?>'
'<testxmlns="http://openstack.org/quantum/api/v2.0"'
'xmlns:quantum="http://openstack.org/quantum/api/v2.0"'
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
'pass</test>'
)
self.assertEqual(result, expected)
class RequestDeserializerTest(testtools.TestCase): class RequestDeserializerTest(testtools.TestCase):
def setUp(self): def setUp(self):
@ -293,13 +185,7 @@ class RequestDeserializerTest(testtools.TestCase):
def deserialize(self, data, action='default'): def deserialize(self, data, action='default'):
return 'pew_json' return 'pew_json'
class XMLDeserializer(object): self.body_deserializers = {'application/json': JSONDeserializer()}
def deserialize(self, data, action='default'):
return 'pew_xml'
self.body_deserializers = {
'application/json': JSONDeserializer(),
'application/xml': XMLDeserializer()}
self.deserializer = wsgi.RequestDeserializer(self.body_deserializers) self.deserializer = wsgi.RequestDeserializer(self.body_deserializers)
@ -307,15 +193,10 @@ class RequestDeserializerTest(testtools.TestCase):
"""Test RequestDeserializer.get_body_deserializer.""" """Test RequestDeserializer.get_body_deserializer."""
expected_json_serializer = self.deserializer.get_body_deserializer( expected_json_serializer = self.deserializer.get_body_deserializer(
'application/json') 'application/json')
expected_xml_serializer = self.deserializer.get_body_deserializer(
'application/xml')
self.assertEqual( self.assertEqual(
expected_json_serializer, expected_json_serializer,
self.body_deserializers['application/json']) self.body_deserializers['application/json'])
self.assertEqual(
expected_xml_serializer,
self.body_deserializers['application/xml'])
def test_get_expected_content_type(self): def test_get_expected_content_type(self):
"""Test RequestDeserializer.get_expected_content_type.""" """Test RequestDeserializer.get_expected_content_type."""
@ -345,9 +226,9 @@ class RequestDeserializerTest(testtools.TestCase):
self.deserializer, 'get_action_args') as mock_method: self.deserializer, 'get_action_args') as mock_method:
mock_method.return_value = {'action': 'create'} mock_method.return_value = {'action': 'create'}
request = wsgi.Request.blank('/') request = wsgi.Request.blank('/')
request.headers['Accept'] = 'application/xml' request.headers['Accept'] = 'application/json'
deserialized = self.deserializer.deserialize(request) deserialized = self.deserializer.deserialize(request)
expected = ('create', {}, 'application/xml') expected = ('create', {}, 'application/json')
self.assertEqual(expected, deserialized) self.assertEqual(expected, deserialized)
@ -368,17 +249,11 @@ class ResponseSerializerTest(testtools.TestCase):
def serialize(self, data, action='default'): def serialize(self, data, action='default'):
return 'pew_json' return 'pew_json'
class XMLSerializer(object):
def serialize(self, data, action='default'):
return 'pew_xml'
class HeadersSerializer(object): class HeadersSerializer(object):
def serialize(self, response, data, action): def serialize(self, response, data, action):
response.status_int = 404 response.status_int = 404
self.body_serializers = { self.body_serializers = {'application/json': JSONSerializer()}
'application/json': JSONSerializer(),
'application/xml': XMLSerializer()}
self.serializer = wsgi.ResponseSerializer( self.serializer = wsgi.ResponseSerializer(
self.body_serializers, HeadersSerializer()) self.body_serializers, HeadersSerializer())
@ -410,13 +285,6 @@ class ResponseSerializerTest(testtools.TestCase):
self.assertEqual(response.body, 'pew_json') self.assertEqual(response.body, 'pew_json')
self.assertEqual(response.status_int, 404) self.assertEqual(response.status_int, 404)
def test_serialize_xml_response(self):
response = self.serializer.serialize({}, 'application/xml')
self.assertEqual(response.headers['Content-Type'], 'application/xml')
self.assertEqual(response.body, 'pew_xml')
self.assertEqual(response.status_int, 404)
def test_serialize_response_None(self): def test_serialize_response_None(self):
response = self.serializer.serialize( response = self.serializer.serialize(
None, 'application/json') None, 'application/json')
@ -455,12 +323,6 @@ class RequestTest(base.BaseTestCase):
self.assertIsNone(request.get_content_type()) self.assertIsNone(request.get_content_type())
def test_content_type_from_accept(self): def test_content_type_from_accept(self):
request = wsgi.Request.blank('/tests/123')
request.headers["Accept"] = "application/xml"
result = request.best_match_content_type()
self.assertEqual(result, "application/xml")
request = wsgi.Request.blank('/tests/123') request = wsgi.Request.blank('/tests/123')
request.headers["Accept"] = "application/json" request.headers["Accept"] = "application/json"
result = request.best_match_content_type() result = request.best_match_content_type()
@ -468,24 +330,12 @@ class RequestTest(base.BaseTestCase):
self.assertEqual(result, "application/json") self.assertEqual(result, "application/json")
request = wsgi.Request.blank('/tests/123') request = wsgi.Request.blank('/tests/123')
request.headers["Accept"] = "application/xml, application/json" request.headers["Accept"] = ("application/json; q=0.3")
result = request.best_match_content_type() result = request.best_match_content_type()
self.assertEqual(result, "application/json") self.assertEqual(result, "application/json")
request = wsgi.Request.blank('/tests/123')
request.headers["Accept"] = ("application/json; q=0.3, "
"application/xml; q=0.9")
result = request.best_match_content_type()
self.assertEqual(result, "application/xml")
def test_content_type_from_query_extension(self): def test_content_type_from_query_extension(self):
request = wsgi.Request.blank('/tests/123.xml')
result = request.best_match_content_type()
self.assertEqual(result, "application/xml")
request = wsgi.Request.blank('/tests/123.json') request = wsgi.Request.blank('/tests/123.json')
result = request.best_match_content_type() result = request.best_match_content_type()
@ -497,11 +347,11 @@ class RequestTest(base.BaseTestCase):
self.assertEqual(result, "application/json") self.assertEqual(result, "application/json")
def test_content_type_accept_and_query_extension(self): def test_content_type_accept_and_query_extension(self):
request = wsgi.Request.blank('/tests/123.xml') request = wsgi.Request.blank('/tests/123.json')
request.headers["Accept"] = "application/json" request.headers["Accept"] = "application/json"
result = request.best_match_content_type() result = request.best_match_content_type()
self.assertEqual(result, "application/xml") self.assertEqual(result, "application/json")
def test_content_type_accept_default(self): def test_content_type_accept_default(self):
request = wsgi.Request.blank('/tests/123.unsupported') request = wsgi.Request.blank('/tests/123.unsupported')
@ -662,39 +512,6 @@ class JSONDeserializerTest(base.BaseTestCase):
deserializer.deserialize(data), as_dict) deserializer.deserialize(data), as_dict)
class XMLDeserializerTest(base.BaseTestCase):
def test_xml_empty(self):
xml = '<a></a>'
as_dict = {'body': {'a': ''}}
deserializer = wsgi.XMLDeserializer()
self.assertEqual(
deserializer.deserialize(xml), as_dict)
def test_initialization(self):
xml = '<a><b>test</b></a>'
deserializer = wsgi.XMLDeserializer()
self.assertEqual(
{'body': {u'a': {u'b': u'test'}}}, deserializer(xml))
def test_default_raise_Malformed_Exception(self):
"""Verify that exception MalformedRequestBody is raised."""
data_string = ""
deserializer = wsgi.XMLDeserializer()
self.assertRaises(
exception.MalformedRequestBody, deserializer.default, data_string)
def test_xml_with_utf8(self):
xml = '<a>\xe7\xbd\x91\xe7\xbb\x9c</a>'
as_dict = {'body': {'a': u'\u7f51\u7edc'}}
deserializer = wsgi.XMLDeserializer()
self.assertEqual(
deserializer.deserialize(xml), as_dict)
class RequestHeadersDeserializerTest(base.BaseTestCase): class RequestHeadersDeserializerTest(base.BaseTestCase):
def test_default(self): def test_default(self):
@ -811,7 +628,7 @@ class ResourceTest(base.BaseTestCase):
return 'off' return 'off'
resource = wsgi.Resource(Controller(), my_fault_body_function) resource = wsgi.Resource(Controller(), my_fault_body_function)
request = wsgi.Request.blank( request = wsgi.Request.blank(
"/", method='POST', headers={'Content-Type': "xml"}) "/", method='POST', headers={'Content-Type': "json"})
response = resource.dispatch( response = resource.dispatch(
request, action='index', action_args='test') request, action='index', action_args='test')
@ -829,7 +646,7 @@ class ResourceTest(base.BaseTestCase):
def __init__(self): def __init__(self):
self.url = 'http://where.no' self.url = 'http://where.no'
self.environ = 'environ' self.environ = 'environ'
self.body = '{"Content-Type": "xml"}' self.body = '{"Content-Type": "json"}'
def method(self): def method(self):
pass pass
@ -867,212 +684,6 @@ class FaultTest(base.BaseTestCase):
self.assertEqual(415, response.status_int) self.assertEqual(415, response.status_int)
class XMLDictSerializerTest(base.BaseTestCase):
def test_xml(self):
NETWORK = {'network': {'test': None,
'tenant_id': 'test-tenant',
'name': 'net1',
'admin_state_up': True,
'subnets': [],
'dict': {},
'int': 3,
'long': 4L,
'float': 5.0,
'prefix:external': True,
'tests': [{'test1': 'value1'},
{'test2': 2, 'test3': 3}]}}
# XML is:
# <network xmlns="http://openstack.org/quantum/api/v2.0"
# xmlns:prefix="http://xxxx.yy.com"
# xmlns:quantum="http://openstack.org/quantum/api/v2.0"
# xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
# <subnets quantum:type="list" /> # Empty List
# <int quantum:type="int">3</int> # Integer text
# <int quantum:type="long">4</int> # Long text
# <int quantum:type="float">5.0</int> # Float text
# <dict quantum:type="dict" /> # Empty Dict
# <name>net1</name>
# <admin_state_up quantum:type="bool">True</admin_state_up> # Bool
# <test xsi:nil="true" /> # None
# <tenant_id>test-tenant</tenant_id>
# # We must have a namespace defined in root for prefix:external
# <prefix:external quantum:type="bool">True</prefix:external>
# <tests> # List
# <test><test1>value1</test1></test>
# <test><test3 quantum:type="int">3</test3>
# <test2 quantum:type="int">2</test2>
# </test></tests>
# </network>
metadata = attributes.get_attr_metadata()
ns = {'prefix': 'http://xxxx.yy.com'}
metadata[constants.EXT_NS] = ns
metadata['plurals'] = {'tests': 'test'}
serializer = wsgi.XMLDictSerializer(metadata)
result = serializer.serialize(NETWORK)
deserializer = wsgi.XMLDeserializer(metadata)
new_net = deserializer.deserialize(result)['body']
self.assertEqual(NETWORK, new_net)
def test_None(self):
data = None
# Since it is None, we use xsi:nil='true'.
# In addition, we use an
# virtual XML root _v_root to wrap the XML doc.
# XML is:
# <_v_root xsi:nil="true"
# xmlns="http://openstack.org/quantum/api/v2.0"
# xmlns:quantum="http://openstack.org/quantum/api/v2.0"
# xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
serializer = wsgi.XMLDictSerializer(attributes.get_attr_metadata())
result = serializer.serialize(data)
deserializer = wsgi.XMLDeserializer(attributes.get_attr_metadata())
new_data = deserializer.deserialize(result)['body']
self.assertIsNone(new_data)
def test_empty_dic_xml(self):
data = {}
# Since it is an empty dict, we use quantum:type='dict' and
# an empty XML element to represent it. In addition, we use an
# virtual XML root _v_root to wrap the XML doc.
# XML is:
# <_v_root quantum:type="dict"
# xmlns="http://openstack.org/quantum/api/v2.0"
# xmlns:quantum="http://openstack.org/quantum/api/v2.0"
# xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
serializer = wsgi.XMLDictSerializer(attributes.get_attr_metadata())
result = serializer.serialize(data)
deserializer = wsgi.XMLDeserializer(attributes.get_attr_metadata())
new_data = deserializer.deserialize(result)['body']
self.assertEqual(data, new_data)
def test_non_root_one_item_dic_xml(self):
data = {'test1': 1}
# We have a key in this dict, and its value is an integer.
# XML is:
# <test1 quantum:type="int"
# xmlns="http://openstack.org/quantum/api/v2.0"
# xmlns:quantum="http://openstack.org/quantum/api/v2.0"
# xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
# 1</test1>
serializer = wsgi.XMLDictSerializer(attributes.get_attr_metadata())
result = serializer.serialize(data)
deserializer = wsgi.XMLDeserializer(attributes.get_attr_metadata())
new_data = deserializer.deserialize(result)['body']
self.assertEqual(data, new_data)
def test_non_root_two_items_dic_xml(self):
data = {'test1': 1, 'test2': '2'}
# We have no root element in this data, We will use a virtual
# root element _v_root to wrap the doct.
# The XML is:
# <_v_root xmlns="http://openstack.org/quantum/api/v2.0"
# xmlns:quantum="http://openstack.org/quantum/api/v2.0"
# xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
# <test1 quantum:type="int">1</test1><test2>2</test2>
# </_v_root>
serializer = wsgi.XMLDictSerializer(attributes.get_attr_metadata())
result = serializer.serialize(data)
deserializer = wsgi.XMLDeserializer(attributes.get_attr_metadata())
new_data = deserializer.deserialize(result)['body']
self.assertEqual(data, new_data)
def test_xml_root_key_is_list(self):
input_dict = {'servers': ['test-pass']}
serializer = wsgi.XMLDictSerializer(xmlns="fake")
result = serializer.default(input_dict)
result = result.replace('\n', '').replace(' ', '')
expected = (
'<?xmlversion=\'1.0\'encoding=\'UTF-8\'?>'
'<serversxmlns="fake"xmlns:quantum="fake"'
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
'<server>test-pass</server></servers>'
)
self.assertEqual(result, expected)
def test_xml_meta_contains_node_name_list(self):
input_dict = {'servers': ['test-pass']}
servers = {'nodename': 'test',
'item_name': 'test',
'item_key': 'test'}
metadata = {'list_collections': {'servers': servers}}
serializer = wsgi.XMLDictSerializer(xmlns="fake", metadata=metadata)
result = serializer.default(input_dict)
result = result.replace('\n', '').replace(' ', '')
expected = (
'<?xmlversion=\'1.0\'encoding=\'UTF-8\'?>'
'<serversxmlns="fake"xmlns:quantum="fake"'
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
'<server>test-pass</server></servers>'
)
self.assertEqual(result, expected)
def test_xml_meta_contains_node_name_dict(self):
input_dict = {'servers': {'a': {'2': '3'}}}
servers = {'servers': {
'nodename': 'test',
'item_name': 'test',
'item_key': 'test'}}
metadata = {'dict_collections': servers}
serializer = wsgi.XMLDictSerializer(xmlns="fake", metadata=metadata)
result = serializer.default(input_dict)
result = result.replace('\n', '').replace(' ', '')
expected = (
'<?xmlversion=\'1.0\'encoding=\'UTF-8\'?>'
'<serversxmlns="fake"xmlns:quantum="fake"'
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
'<a><2>3</2></a></servers>'
)
self.assertEqual(result, expected)
def test_call(self):
data = {'servers': {'a': {'2': '3'}}}
serializer = wsgi.XMLDictSerializer()
expected = (
'<?xmlversion=\'1.0\'encoding=\'UTF-8\'?>'
'<serversxmlns="http://openstack.org/quantum/api/v2.0"'
'xmlns:quantum="http://openstack.org/quantum/api/v2.0"'
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
'<a><2>3</2></a></servers>'
)
result = serializer(data)
result = result.replace('\n', '').replace(' ', '')
self.assertEqual(expected, result)
def test_xml_with_utf8(self):
data = {'servers': '\xe7\xbd\x91\xe7\xbb\x9c'}
serializer = wsgi.XMLDictSerializer()
expected = (
'<?xmlversion=\'1.0\'encoding=\'UTF-8\'?>'
'<serversxmlns="http://openstack.org/quantum/api/v2.0"'
'xmlns:quantum="http://openstack.org/quantum/api/v2.0"'
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
'\xe7\xbd\x91\xe7\xbb\x9c</servers>'
)
result = serializer(data)
result = result.replace('\n', '').replace(' ', '')
self.assertEqual(expected, result)
def test_xml_with_unicode(self):
data = {'servers': u'\u7f51\u7edc'}
serializer = wsgi.XMLDictSerializer()
expected = (
'<?xmlversion=\'1.0\'encoding=\'UTF-8\'?>'
'<serversxmlns="http://openstack.org/quantum/api/v2.0"'
'xmlns:quantum="http://openstack.org/quantum/api/v2.0"'
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
'\xe7\xbd\x91\xe7\xbb\x9c</servers>'
)
result = serializer(data)
result = result.replace('\n', '').replace(' ', '')
self.assertEqual(expected, result)
class TestWSGIServerWithSSL(base.BaseTestCase): class TestWSGIServerWithSSL(base.BaseTestCase):
"""WSGI server tests.""" """WSGI server tests."""

View File

@ -15,7 +15,6 @@
import testtools import testtools
from neutron.api.v2 import attributes
from neutron.db import api as db_api from neutron.db import api as db_api
# Import all data models # Import all data models
from neutron.db.migration.models import head # noqa from neutron.db.migration.models import head # noqa
@ -81,11 +80,8 @@ class WebTestCase(SqlTestCase):
def setUp(self): def setUp(self):
super(WebTestCase, self).setUp() super(WebTestCase, self).setUp()
json_deserializer = wsgi.JSONDeserializer() json_deserializer = wsgi.JSONDeserializer()
xml_deserializer = wsgi.XMLDeserializer(
attributes.get_attr_metadata())
self._deserializers = { self._deserializers = {
'application/json': json_deserializer, 'application/json': json_deserializer,
'application/xml': xml_deserializer,
} }
def deserialize(self, response): def deserialize(self, response):
@ -95,8 +91,7 @@ class WebTestCase(SqlTestCase):
def serialize(self, data): def serialize(self, data):
ctype = 'application/%s' % self.fmt ctype = 'application/%s' % self.fmt
result = wsgi.Serializer( result = wsgi.Serializer().serialize(data, ctype)
attributes.get_attr_metadata()).serialize(data, ctype)
return result return result

View File

@ -24,8 +24,6 @@ import socket
import ssl import ssl
import sys import sys
import time import time
from xml.etree import ElementTree as etree
from xml.parsers import expat
import eventlet.wsgi import eventlet.wsgi
eventlet.patcher.monkey_patch(all=False, socket=True, thread=True) eventlet.patcher.monkey_patch(all=False, socket=True, thread=True)
@ -34,7 +32,6 @@ import routes.middleware
import webob.dec import webob.dec
import webob.exc import webob.exc
from neutron.common import constants
from neutron.common import exceptions as exception from neutron.common import exceptions as exception
from neutron import context from neutron import context
from neutron.db import api from neutron.db import api
@ -313,7 +310,7 @@ class Request(webob.Request):
"""Determine the most acceptable content-type. """Determine the most acceptable content-type.
Based on: Based on:
1) URI extension (.json/.xml) 1) URI extension (.json)
2) Content-type header 2) Content-type header
3) Accept* headers 3) Accept* headers
""" """
@ -321,21 +318,21 @@ class Request(webob.Request):
parts = self.path.rsplit('.', 1) parts = self.path.rsplit('.', 1)
if len(parts) > 1: if len(parts) > 1:
_format = parts[1] _format = parts[1]
if _format in ['json', 'xml']: if _format in ['json']:
return 'application/{0}'.format(_format) return 'application/{0}'.format(_format)
#Then look up content header #Then look up content header
type_from_header = self.get_content_type() type_from_header = self.get_content_type()
if type_from_header: if type_from_header:
return type_from_header return type_from_header
ctypes = ['application/json', 'application/xml'] ctypes = ['application/json']
#Finally search in Accept-* headers #Finally search in Accept-* headers
bm = self.accept.best_match(ctypes) bm = self.accept.best_match(ctypes)
return bm or 'application/json' return bm or 'application/json'
def get_content_type(self): def get_content_type(self):
allowed_types = ("application/xml", "application/json") allowed_types = ("application/json")
if "Content-Type" not in self.headers: if "Content-Type" not in self.headers:
LOG.debug(_("Missing Content-Type")) LOG.debug(_("Missing Content-Type"))
return None return None
@ -394,154 +391,6 @@ class JSONDictSerializer(DictSerializer):
return jsonutils.dumps(data, default=sanitizer) return jsonutils.dumps(data, default=sanitizer)
class XMLDictSerializer(DictSerializer):
def __init__(self, metadata=None, xmlns=None):
"""Object initialization.
:param metadata: information needed to deserialize xml into
a dictionary.
:param xmlns: XML namespace to include with serialized xml
"""
super(XMLDictSerializer, self).__init__()
self.metadata = metadata or {}
if not xmlns:
xmlns = self.metadata.get('xmlns')
if not xmlns:
xmlns = constants.XML_NS_V20
self.xmlns = xmlns
def default(self, data):
"""Return data as XML string.
:param data: expect data to contain a single key as XML root, or
contain another '*_links' key as atom links. Other
case will use 'VIRTUAL_ROOT_KEY' as XML root.
"""
try:
links = None
has_atom = False
if data is None:
root_key = constants.VIRTUAL_ROOT_KEY
root_value = None
else:
link_keys = [k for k in data.iterkeys() or []
if k.endswith('_links')]
if link_keys:
links = data.pop(link_keys[0], None)
has_atom = True
root_key = (len(data) == 1 and
data.keys()[0] or constants.VIRTUAL_ROOT_KEY)
root_value = data.get(root_key, data)
doc = etree.Element("_temp_root")
used_prefixes = []
self._to_xml_node(doc, self.metadata, root_key,
root_value, used_prefixes)
if links:
self._create_link_nodes(list(doc)[0], links)
return self.to_xml_string(list(doc)[0], used_prefixes, has_atom)
except AttributeError as e:
LOG.exception(str(e))
return ''
def __call__(self, data):
# Provides a migration path to a cleaner WSGI layer, this
# "default" stuff and extreme extensibility isn't being used
# like originally intended
return self.default(data)
def to_xml_string(self, node, used_prefixes, has_atom=False):
self._add_xmlns(node, used_prefixes, has_atom)
return etree.tostring(node, encoding='UTF-8')
#NOTE (ameade): the has_atom should be removed after all of the
# xml serializers and view builders have been updated to the current
# spec that required all responses include the xmlns:atom, the has_atom
# flag is to prevent current tests from breaking
def _add_xmlns(self, node, used_prefixes, has_atom=False):
node.set('xmlns', self.xmlns)
node.set(constants.TYPE_XMLNS, self.xmlns)
if has_atom:
node.set(constants.ATOM_XMLNS, constants.ATOM_NAMESPACE)
node.set(constants.XSI_NIL_ATTR, constants.XSI_NAMESPACE)
ext_ns = self.metadata.get(constants.EXT_NS, {})
ext_ns_bc = self.metadata.get(constants.EXT_NS_COMP, {})
for prefix in used_prefixes:
if prefix in ext_ns:
node.set('xmlns:' + prefix, ext_ns[prefix])
if prefix in ext_ns_bc:
node.set('xmlns:' + prefix, ext_ns_bc[prefix])
def _to_xml_node(self, parent, metadata, nodename, data, used_prefixes):
"""Recursive method to convert data members to XML nodes."""
result = etree.SubElement(parent, nodename)
if ":" in nodename:
used_prefixes.append(nodename.split(":", 1)[0])
#TODO(bcwaldon): accomplish this without a type-check
if isinstance(data, list):
if not data:
result.set(
constants.TYPE_ATTR,
constants.TYPE_LIST)
return result
singular = metadata.get('plurals', {}).get(nodename, None)
if singular is None:
if nodename.endswith('s'):
singular = nodename[:-1]
else:
singular = 'item'
for item in data:
self._to_xml_node(result, metadata, singular, item,
used_prefixes)
#TODO(bcwaldon): accomplish this without a type-check
elif isinstance(data, dict):
if not data:
result.set(
constants.TYPE_ATTR,
constants.TYPE_DICT)
return result
attrs = metadata.get('attributes', {}).get(nodename, {})
for k, v in data.items():
if k in attrs:
result.set(k, str(v))
else:
self._to_xml_node(result, metadata, k, v,
used_prefixes)
elif data is None:
result.set(constants.XSI_ATTR, 'true')
else:
if isinstance(data, bool):
result.set(
constants.TYPE_ATTR,
constants.TYPE_BOOL)
elif isinstance(data, int):
result.set(
constants.TYPE_ATTR,
constants.TYPE_INT)
elif isinstance(data, long):
result.set(
constants.TYPE_ATTR,
constants.TYPE_LONG)
elif isinstance(data, float):
result.set(
constants.TYPE_ATTR,
constants.TYPE_FLOAT)
LOG.debug(_("Data %(data)s type is %(type)s"),
{'data': data,
'type': type(data)})
if isinstance(data, str):
result.text = unicode(data, 'utf-8')
else:
result.text = unicode(data)
return result
def _create_link_nodes(self, xml_doc, links):
for link in links:
link_node = etree.SubElement(xml_doc, 'atom:link')
link_node.set('rel', link['rel'])
link_node.set('href', link['href'])
class ResponseHeaderSerializer(ActionDispatcher): class ResponseHeaderSerializer(ActionDispatcher):
"""Default response headers serialization.""" """Default response headers serialization."""
@ -557,7 +406,6 @@ class ResponseSerializer(object):
def __init__(self, body_serializers=None, headers_serializer=None): def __init__(self, body_serializers=None, headers_serializer=None):
self.body_serializers = { self.body_serializers = {
'application/xml': XMLDictSerializer(),
'application/json': JSONDictSerializer(), 'application/json': JSONDictSerializer(),
} }
self.body_serializers.update(body_serializers or {}) self.body_serializers.update(body_serializers or {})
@ -616,156 +464,6 @@ class JSONDeserializer(TextDeserializer):
return {'body': self._from_json(datastring)} return {'body': self._from_json(datastring)}
class ProtectedXMLParser(etree.XMLParser):
def __init__(self, *args, **kwargs):
etree.XMLParser.__init__(self, *args, **kwargs)
self._parser.StartDoctypeDeclHandler = self.start_doctype_decl
def start_doctype_decl(self, name, sysid, pubid, internal):
raise ValueError(_("Inline DTD forbidden"))
def doctype(self, name, pubid, system):
raise ValueError(_("Inline DTD forbidden"))
class XMLDeserializer(TextDeserializer):
def __init__(self, metadata=None):
"""Object initialization.
:param metadata: information needed to deserialize xml into
a dictionary.
"""
super(XMLDeserializer, self).__init__()
self.metadata = metadata or {}
xmlns = self.metadata.get('xmlns')
if not xmlns:
xmlns = constants.XML_NS_V20
self.xmlns = xmlns
def _get_key(self, tag):
tags = tag.split("}", 1)
if len(tags) == 2:
ns = tags[0][1:]
bare_tag = tags[1]
ext_ns = self.metadata.get(constants.EXT_NS, {})
if ns == self.xmlns:
return bare_tag
for prefix, _ns in ext_ns.items():
if ns == _ns:
return prefix + ":" + bare_tag
ext_ns_bc = self.metadata.get(constants.EXT_NS_COMP, {})
for prefix, _ns in ext_ns_bc.items():
if ns == _ns:
return prefix + ":" + bare_tag
else:
return tag
def _get_links(self, root_tag, node):
link_nodes = node.findall(constants.ATOM_LINK_NOTATION)
root_tag = self._get_key(node.tag)
link_key = "%s_links" % root_tag
link_list = []
for link in link_nodes:
link_list.append({'rel': link.get('rel'),
'href': link.get('href')})
# Remove link node in order to avoid link node process as
# an item in _from_xml_node
node.remove(link)
return link_list and {link_key: link_list} or {}
def _parseXML(self, text):
parser = ProtectedXMLParser()
parser.feed(text)
return parser.close()
def _from_xml(self, datastring):
if datastring is None:
return None
plurals = set(self.metadata.get('plurals', {}))
try:
node = self._parseXML(datastring)
root_tag = self._get_key(node.tag)
# Deserialize link node was needed by unit test for verifying
# the request's response
links = self._get_links(root_tag, node)
result = self._from_xml_node(node, plurals)
# root_tag = constants.VIRTUAL_ROOT_KEY and links is not None
# is not possible because of the way data are serialized.
if root_tag == constants.VIRTUAL_ROOT_KEY:
return result
return dict({root_tag: result}, **links)
except Exception as e:
with excutils.save_and_reraise_exception():
parseError = False
# Python2.7
if (hasattr(etree, 'ParseError') and
isinstance(e, getattr(etree, 'ParseError'))):
parseError = True
# Python2.6
elif isinstance(e, expat.ExpatError):
parseError = True
if parseError:
msg = _("Cannot understand XML")
raise exception.MalformedRequestBody(reason=msg)
def _from_xml_node(self, node, listnames):
"""Convert a minidom node to a simple Python type.
:param listnames: list of XML node names whose subnodes should
be considered list items.
"""
attrNil = node.get(str(etree.QName(constants.XSI_NAMESPACE, "nil")))
attrType = node.get(str(etree.QName(
self.metadata.get('xmlns'), "type")))
if (attrNil and attrNil.lower() == 'true'):
return None
elif not len(node) and not node.text:
if (attrType and attrType == constants.TYPE_DICT):
return {}
elif (attrType and attrType == constants.TYPE_LIST):
return []
else:
return ''
elif (len(node) == 0 and node.text):
converters = {constants.TYPE_BOOL:
lambda x: x.lower() == 'true',
constants.TYPE_INT:
lambda x: int(x),
constants.TYPE_LONG:
lambda x: long(x),
constants.TYPE_FLOAT:
lambda x: float(x)}
if attrType and attrType in converters:
return converters[attrType](node.text)
else:
return node.text
elif self._get_key(node.tag) in listnames:
return [self._from_xml_node(n, listnames) for n in node]
else:
result = dict()
for attr in node.keys():
if (attr == 'xmlns' or
attr.startswith('xmlns:') or
attr == constants.XSI_ATTR or
attr == constants.TYPE_ATTR):
continue
result[self._get_key(attr)] = node.get(attr)
children = list(node)
for child in children:
result[self._get_key(child.tag)] = self._from_xml_node(
child, listnames)
return result
def default(self, datastring):
return {'body': self._from_xml(datastring)}
def __call__(self, datastring):
# Adding a migration path to allow us to remove unncessary classes
return self.default(datastring)
class RequestHeadersDeserializer(ActionDispatcher): class RequestHeadersDeserializer(ActionDispatcher):
"""Default request headers deserializer.""" """Default request headers deserializer."""
@ -781,7 +479,6 @@ class RequestDeserializer(object):
def __init__(self, body_deserializers=None, headers_deserializer=None): def __init__(self, body_deserializers=None, headers_deserializer=None):
self.body_deserializers = { self.body_deserializers = {
'application/xml': XMLDeserializer(),
'application/json': JSONDeserializer(), 'application/json': JSONDeserializer(),
} }
self.body_deserializers.update(body_deserializers or {}) self.body_deserializers.update(body_deserializers or {})
@ -1057,10 +754,6 @@ class Resource(Application):
self.deserializer = deserializer or RequestDeserializer() self.deserializer = deserializer or RequestDeserializer()
self.serializer = serializer or ResponseSerializer() self.serializer = serializer or ResponseSerializer()
self._fault_body_function = fault_body_function self._fault_body_function = fault_body_function
# use serializer's xmlns for populating Fault generator xmlns
xml_serializer = self.serializer.body_serializers['application/xml']
if hasattr(xml_serializer, 'xmlns'):
self._xmlns = xml_serializer.xmlns
@webob.dec.wsgify(RequestClass=Request) @webob.dec.wsgify(RequestClass=Request)
def __call__(self, request): def __call__(self, request):
@ -1074,26 +767,21 @@ class Resource(Application):
except exception.InvalidContentType: except exception.InvalidContentType:
msg = _("Unsupported Content-Type") msg = _("Unsupported Content-Type")
LOG.exception(_("InvalidContentType: %s"), msg) LOG.exception(_("InvalidContentType: %s"), msg)
return Fault(webob.exc.HTTPBadRequest(explanation=msg), return Fault(webob.exc.HTTPBadRequest(explanation=msg))
self._xmlns)
except exception.MalformedRequestBody: except exception.MalformedRequestBody:
msg = _("Malformed request body") msg = _("Malformed request body")
LOG.exception(_("MalformedRequestBody: %s"), msg) LOG.exception(_("MalformedRequestBody: %s"), msg)
return Fault(webob.exc.HTTPBadRequest(explanation=msg), return Fault(webob.exc.HTTPBadRequest(explanation=msg))
self._xmlns)
try: try:
action_result = self.dispatch(request, action, args) action_result = self.dispatch(request, action, args)
except webob.exc.HTTPException as ex: except webob.exc.HTTPException as ex:
LOG.info(_("HTTP exception thrown: %s"), unicode(ex)) LOG.info(_("HTTP exception thrown: %s"), unicode(ex))
action_result = Fault(ex, action_result = Fault(ex, self._fault_body_function)
self._xmlns,
self._fault_body_function)
except Exception: except Exception:
LOG.exception(_("Internal error")) LOG.exception(_("Internal error"))
# Do not include the traceback to avoid returning it to clients. # Do not include the traceback to avoid returning it to clients.
action_result = Fault(webob.exc.HTTPServerError(), action_result = Fault(webob.exc.HTTPServerError(),
self._xmlns,
self._fault_body_function) self._fault_body_function)
if isinstance(action_result, dict) or action_result is None: if isinstance(action_result, dict) or action_result is None:
@ -1124,8 +812,7 @@ class Resource(Application):
return controller_method(request=request, **action_args) return controller_method(request=request, **action_args)
except TypeError as exc: except TypeError as exc:
LOG.exception(exc) LOG.exception(exc)
return Fault(webob.exc.HTTPBadRequest(), return Fault(webob.exc.HTTPBadRequest())
self._xmlns)
def _default_body_function(wrapped_exc): def _default_body_function(wrapped_exc):
@ -1142,11 +829,10 @@ def _default_body_function(wrapped_exc):
class Fault(webob.exc.HTTPException): class Fault(webob.exc.HTTPException):
"""Generates an HTTP response from a webob HTTP exception.""" """Generates an HTTP response from a webob HTTP exception."""
def __init__(self, exception, xmlns=None, body_function=None): def __init__(self, exception, body_function=None):
"""Creates a Fault for the given webob.exc.exception.""" """Creates a Fault for the given webob.exc.exception."""
self.wrapped_exc = exception self.wrapped_exc = exception
self.status_int = self.wrapped_exc.status_int self.status_int = self.wrapped_exc.status_int
self._xmlns = xmlns
self._body_function = body_function or _default_body_function self._body_function = body_function or _default_body_function
@webob.dec.wsgify(RequestClass=Request) @webob.dec.wsgify(RequestClass=Request)
@ -1154,10 +840,8 @@ class Fault(webob.exc.HTTPException):
"""Generate a WSGI response based on the exception passed to ctor.""" """Generate a WSGI response based on the exception passed to ctor."""
# Replace the body with fault details. # Replace the body with fault details.
fault_data, metadata = self._body_function(self.wrapped_exc) fault_data, metadata = self._body_function(self.wrapped_exc)
xml_serializer = XMLDictSerializer(metadata, self._xmlns)
content_type = req.best_match_content_type() content_type = req.best_match_content_type()
serializer = { serializer = {
'application/xml': xml_serializer,
'application/json': JSONDictSerializer(), 'application/json': JSONDictSerializer(),
}[content_type] }[content_type]
@ -1200,8 +884,7 @@ class Controller(object):
else: else:
status = 200 status = 200
content_type = req.best_match_content_type() content_type = req.best_match_content_type()
default_xmlns = self.get_default_xmlns(req) body = self._serialize(result, content_type)
body = self._serialize(result, content_type, default_xmlns)
response = webob.Response(status=status, response = webob.Response(status=status,
content_type=content_type, content_type=content_type,
@ -1213,7 +896,7 @@ class Controller(object):
else: else:
return result return result
def _serialize(self, data, content_type, default_xmlns): def _serialize(self, data, content_type):
"""Serialize the given dict to the provided content_type. """Serialize the given dict to the provided content_type.
Uses self._serialization_metadata if it exists, which is a dict mapping Uses self._serialization_metadata if it exists, which is a dict mapping
@ -1222,7 +905,7 @@ class Controller(object):
""" """
_metadata = getattr(type(self), '_serialization_metadata', {}) _metadata = getattr(type(self), '_serialization_metadata', {})
serializer = Serializer(_metadata, default_xmlns) serializer = Serializer(_metadata)
try: try:
return serializer.serialize(data, content_type) return serializer.serialize(data, content_type)
except exception.InvalidContentType: except exception.InvalidContentType:
@ -1240,17 +923,13 @@ class Controller(object):
serializer = Serializer(_metadata) serializer = Serializer(_metadata)
return serializer.deserialize(data, content_type)['body'] return serializer.deserialize(data, content_type)['body']
def get_default_xmlns(self, req):
"""Provide the XML namespace to use if none is otherwise specified."""
return None
# NOTE(salvatore-orlando): this class will go once the # NOTE(salvatore-orlando): this class will go once the
# extension API framework is updated # extension API framework is updated
class Serializer(object): class Serializer(object):
"""Serializes and deserializes dictionaries to certain MIME types.""" """Serializes and deserializes dictionaries to certain MIME types."""
def __init__(self, metadata=None, default_xmlns=None): def __init__(self, metadata=None):
"""Create a serializer based on the given WSGI environment. """Create a serializer based on the given WSGI environment.
'metadata' is an optional dict mapping MIME types to information 'metadata' is an optional dict mapping MIME types to information
@ -1258,12 +937,10 @@ class Serializer(object):
""" """
self.metadata = metadata or {} self.metadata = metadata or {}
self.default_xmlns = default_xmlns
def _get_serialize_handler(self, content_type): def _get_serialize_handler(self, content_type):
handlers = { handlers = {
'application/json': JSONDictSerializer(), 'application/json': JSONDictSerializer(),
'application/xml': XMLDictSerializer(self.metadata),
} }
try: try:
@ -1290,7 +967,6 @@ class Serializer(object):
def get_deserialize_handler(self, content_type): def get_deserialize_handler(self, content_type):
handlers = { handlers = {
'application/json': JSONDeserializer(), 'application/json': JSONDeserializer(),
'application/xml': XMLDeserializer(self.metadata),
} }
try: try: