From 306ce8252e52b116f1df55733dbc4e9c0b3a7d16 Mon Sep 17 00:00:00 2001 From: Zhiteng Huang Date: Wed, 30 Apr 2014 23:47:55 +0800 Subject: [PATCH] Add XML deserializer for qos_manage delete_keys API XML deserializer is missing for delete_keys API resulting XML format request not being correctly handled by QoS Manage API extension. This patch adds XML deserializer for the API. Also add unittest for qos_manage XML serializer and deserilizer. Change-Id: I7b3ac6822c52f11b08d767aa55b7107bd0333c36 Closes-bug: #1312553 --- cinder/api/contrib/qos_specs_manage.py | 22 ++++ cinder/api/schemas/v1.1/qos_association.rng | 8 ++ cinder/api/schemas/v1.1/qos_associations.rng | 5 + cinder/api/schemas/v1.1/qos_spec.rng | 16 +++ cinder/api/schemas/v1.1/qos_specs.rng | 5 + .../api/contrib/test_qos_specs_manage.py | 107 ++++++++++++++++++ 6 files changed, 163 insertions(+) create mode 100644 cinder/api/schemas/v1.1/qos_association.rng create mode 100644 cinder/api/schemas/v1.1/qos_associations.rng create mode 100644 cinder/api/schemas/v1.1/qos_spec.rng create mode 100644 cinder/api/schemas/v1.1/qos_specs.rng diff --git a/cinder/api/contrib/qos_specs_manage.py b/cinder/api/contrib/qos_specs_manage.py index 1d5001719..8420c2a4c 100644 --- a/cinder/api/contrib/qos_specs_manage.py +++ b/cinder/api/contrib/qos_specs_manage.py @@ -26,6 +26,7 @@ from cinder import exception from cinder.openstack.common import log as logging from cinder.openstack.common import strutils from cinder import rpc +from cinder import utils from cinder.volume import qos_specs @@ -61,6 +62,26 @@ class QoSSpecsTemplate(xmlutil.TemplateBuilder): return xmlutil.MasterTemplate(root, 1) +class QoSSpecsKeyDeserializer(wsgi.XMLDeserializer): + def _extract_keys(self, key_node): + keys = [] + for key in key_node.childNodes: + key_name = key.tagName + keys.append(key_name) + + return keys + + def default(self, string): + dom = utils.safe_minidom_parse_string(string) + key_node = self.find_first_child_named(dom, 'keys') + if not key_node: + LOG.info(_("Unable to parse XML input.")) + msg = _("Unable to parse XML request. " + "Please provide XML in correct format.") + raise webob.exc.HTTPBadRequest(explanation=msg) + return {'body': {'keys': self._extract_keys(key_node)}} + + class AssociationsTemplate(xmlutil.TemplateBuilder): def construct(self): root = xmlutil.TemplateElement('qos_associations') @@ -225,6 +246,7 @@ class QoSSpecsController(wsgi.Controller): return webob.Response(status_int=202) + @wsgi.deserializers(xml=QoSSpecsKeyDeserializer) def delete_keys(self, req, id, body): """Deletes specified keys in qos specs.""" context = req.environ['cinder.context'] diff --git a/cinder/api/schemas/v1.1/qos_association.rng b/cinder/api/schemas/v1.1/qos_association.rng new file mode 100644 index 000000000..20d975c01 --- /dev/null +++ b/cinder/api/schemas/v1.1/qos_association.rng @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/cinder/api/schemas/v1.1/qos_associations.rng b/cinder/api/schemas/v1.1/qos_associations.rng new file mode 100644 index 000000000..3d218f881 --- /dev/null +++ b/cinder/api/schemas/v1.1/qos_associations.rng @@ -0,0 +1,5 @@ + + + + + diff --git a/cinder/api/schemas/v1.1/qos_spec.rng b/cinder/api/schemas/v1.1/qos_spec.rng new file mode 100644 index 000000000..c82741fc3 --- /dev/null +++ b/cinder/api/schemas/v1.1/qos_spec.rng @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/cinder/api/schemas/v1.1/qos_specs.rng b/cinder/api/schemas/v1.1/qos_specs.rng new file mode 100644 index 000000000..974e7debc --- /dev/null +++ b/cinder/api/schemas/v1.1/qos_specs.rng @@ -0,0 +1,5 @@ + + + + + diff --git a/cinder/tests/api/contrib/test_qos_specs_manage.py b/cinder/tests/api/contrib/test_qos_specs_manage.py index 8abf1d070..84ce76fa6 100644 --- a/cinder/tests/api/contrib/test_qos_specs_manage.py +++ b/cinder/tests/api/contrib/test_qos_specs_manage.py @@ -14,11 +14,13 @@ # License for the specific language governing permissions and limitations # under the License. +from lxml import etree from xml.dom import minidom import webob from cinder.api.contrib import qos_specs_manage +from cinder.api import xmlutil from cinder import exception from cinder import test from cinder.tests.api import fakes @@ -598,3 +600,108 @@ class QoSSpecManageApiTest(test.TestCase): '/v2/fake/qos-specs/222/disassociate_all') self.assertRaises(webob.exc.HTTPInternalServerError, self.controller.disassociate_all, req, '222') + + +class TestQoSSpecsTemplate(test.TestCase): + def setUp(self): + super(TestQoSSpecsTemplate, self).setUp() + self.serializer = qos_specs_manage.QoSSpecsTemplate() + + def test_qos_specs_serializer(self): + fixture = { + "qos_specs": [ + { + "specs": { + "key1": "v1", + "key2": "v2", + }, + "consumer": "back-end", + "name": "qos-2", + "id": "61e7b72f-ef15-46d9-b00e-b80f699999d0" + }, + { + "specs": {"total_iops_sec": "200"}, + "consumer": "front-end", + "name": "qos-1", + "id": "e44bba5e-b629-4b96-9aa3-0404753a619b" + } + ] + } + + output = self.serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'qos_specs') + qos_elems = root.findall("qos_spec") + self.assertEqual(len(qos_elems), 2) + for i, qos_elem in enumerate(qos_elems): + qos_dict = fixture['qos_specs'][i] + + # check qos_spec attributes + for key in ['name', 'id', 'consumer']: + self.assertEqual(qos_elem.get(key), str(qos_dict[key])) + + # check specs + specs = qos_elem.find("specs") + new_dict = {} + for element in specs.iter(tag=etree.Element): + # skip root element for specs + if element.tag == "specs": + continue + new_dict.update({element.tag: element.text}) + + self.assertDictMatch(new_dict, qos_dict['specs']) + + +class TestAssociationsTemplate(test.TestCase): + def setUp(self): + super(TestAssociationsTemplate, self).setUp() + self.serializer = qos_specs_manage.AssociationsTemplate() + + def test_qos_associations_serializer(self): + fixture = { + "qos_associations": [ + { + "association_type": "volume_type", + "name": "type-4", + "id": "14d54d29-51a4-4046-9f6f-cf9800323563" + }, + { + "association_type": "volume_type", + "name": "type-2", + "id": "3689ce83-308d-4ba1-8faf-7f1be04a282b"} + ] + } + + output = self.serializer.serialize(fixture) + root = etree.XML(output) + xmlutil.validate_schema(root, 'qos_associations') + association_elems = root.findall("associations") + self.assertEqual(len(association_elems), 2) + for i, association_elem in enumerate(association_elems): + association_dict = fixture['qos_associations'][i] + + # check qos_spec attributes + for key in ['name', 'id', 'association_type']: + self.assertEqual(association_elem.get(key), + str(association_dict[key])) + + +class TestQoSSpecsKeyDeserializer(test.TestCase): + def setUp(self): + super(TestQoSSpecsKeyDeserializer, self).setUp() + self.deserializer = qos_specs_manage.QoSSpecsKeyDeserializer() + + def test_keys(self): + self_request = """ +""" + request = self.deserializer.deserialize(self_request) + expected = { + "keys": ["xyz", "abc"] + } + self.assertEqual(request['body'], expected) + + def test_bad_format(self): + self_request = """ +""" + self.assertRaises(webob.exc.HTTPBadRequest, + self.deserializer.deserialize, self_request) -- 2.45.2