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
This commit is contained in:
parent
6168d361cf
commit
306ce8252e
@ -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']
|
||||
|
8
cinder/api/schemas/v1.1/qos_association.rng
Normal file
8
cinder/api/schemas/v1.1/qos_association.rng
Normal file
@ -0,0 +1,8 @@
|
||||
<element name="associations" xmlns="http://relaxng.org/ns/structure/1.0">
|
||||
<attribute name="name"> <text/> </attribute>
|
||||
<attribute name="id"> <text/> </attribute>
|
||||
<attribute name="association_type"> <text/> </attribute>
|
||||
<zeroOrMore>
|
||||
<externalRef href="../atom-link.rng"/>
|
||||
</zeroOrMore>
|
||||
</element>
|
5
cinder/api/schemas/v1.1/qos_associations.rng
Normal file
5
cinder/api/schemas/v1.1/qos_associations.rng
Normal file
@ -0,0 +1,5 @@
|
||||
<element name="qos_associations" xmlns="http://relaxng.org/ns/structure/1.0">
|
||||
<zeroOrMore>
|
||||
<externalRef href="qos_association.rng"/>
|
||||
</zeroOrMore>
|
||||
</element>
|
16
cinder/api/schemas/v1.1/qos_spec.rng
Normal file
16
cinder/api/schemas/v1.1/qos_spec.rng
Normal file
@ -0,0 +1,16 @@
|
||||
<element name="qos_spec" xmlns="http://relaxng.org/ns/structure/1.0">
|
||||
<attribute name="name"> <text/> </attribute>
|
||||
<attribute name="id"> <text/> </attribute>
|
||||
<attribute name="consumer"> <text/> </attribute>
|
||||
<element name="specs">
|
||||
<zeroOrMore>
|
||||
<element>
|
||||
<anyName/>
|
||||
<text/>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
<zeroOrMore>
|
||||
<externalRef href="../atom-link.rng"/>
|
||||
</zeroOrMore>
|
||||
</element>
|
5
cinder/api/schemas/v1.1/qos_specs.rng
Normal file
5
cinder/api/schemas/v1.1/qos_specs.rng
Normal file
@ -0,0 +1,5 @@
|
||||
<element name="qos_specs" xmlns="http://relaxng.org/ns/structure/1.0">
|
||||
<zeroOrMore>
|
||||
<externalRef href="qos_spec.rng"/>
|
||||
</zeroOrMore>
|
||||
</element>
|
@ -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 = """
|
||||
<keys><xyz /><abc /></keys>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"keys": ["xyz", "abc"]
|
||||
}
|
||||
self.assertEqual(request['body'], expected)
|
||||
|
||||
def test_bad_format(self):
|
||||
self_request = """
|
||||
<qos_specs><keys><xyz /><abc /></keys></qos_specs>"""
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.deserializer.deserialize, self_request)
|
||||
|
Loading…
Reference in New Issue
Block a user