Fix xml metadata for volumes extension.

Fixes bug 1040891

Change-Id: I3a5d46af18f764e86ab457071d2b3afafdcdaa24
Signed-off-by: Matthew Treinish <treinish@linux.vnet.ibm.com>
This commit is contained in:
Matthew Treinish
2012-08-30 11:47:56 -04:00
parent b6f5d60f52
commit eb86e97ceb
2 changed files with 181 additions and 9 deletions

View File

@@ -17,6 +17,7 @@
import webob import webob
from webob import exc from webob import exc
from xml.dom import minidom
from nova.api.openstack import common from nova.api.openstack import common
from nova.api.openstack import extensions from nova.api.openstack import extensions
@@ -74,10 +75,8 @@ def _translate_volume_summary_view(context, vol):
LOG.audit(_("vol=%s"), vol, context=context) LOG.audit(_("vol=%s"), vol, context=context)
if vol.get('volume_metadata'): if vol.get('volume_metadata'):
meta_dict = {} metadata = vol.get('volume_metadata')
for i in vol['volume_metadata']: d['metadata'] = dict((item['key'], item['value']) for item in metadata)
meta_dict[i['key']] = i['value']
d['metadata'] = meta_dict
else: else:
d['metadata'] = {} d['metadata'] = {}
@@ -100,8 +99,8 @@ def make_volume(elem):
selector='attachments') selector='attachments')
make_attachment(attachment) make_attachment(attachment)
metadata = xmlutil.make_flat_dict('metadata') # Attach metadata node
elem.append(metadata) elem.append(common.MetadataTemplate())
class VolumeTemplate(xmlutil.TemplateBuilder): class VolumeTemplate(xmlutil.TemplateBuilder):
@@ -119,6 +118,47 @@ class VolumesTemplate(xmlutil.TemplateBuilder):
return xmlutil.MasterTemplate(root, 1) return xmlutil.MasterTemplate(root, 1)
class CommonDeserializer(wsgi.MetadataXMLDeserializer):
"""Common deserializer to handle xml-formatted volume requests.
Handles standard volume attributes as well as the optional metadata
attribute
"""
metadata_deserializer = common.MetadataXMLDeserializer()
def _extract_volume(self, node):
"""Marshal the volume attribute of a parsed request."""
volume = {}
volume_node = self.find_first_child_named(node, 'volume')
attributes = ['display_name', 'display_description', 'size',
'volume_type', 'availability_zone']
for attr in attributes:
if volume_node.getAttribute(attr):
volume[attr] = volume_node.getAttribute(attr)
metadata_node = self.find_first_child_named(volume_node, 'metadata')
if metadata_node is not None:
volume['metadata'] = self.extract_metadata(metadata_node)
return volume
class CreateDeserializer(CommonDeserializer):
"""Deserializer to handle xml-formatted create volume requests.
Handles standard volume attributes as well as the optional metadata
attribute
"""
def default(self, string):
"""Deserialize an xml-formatted volume create request."""
dom = minidom.parseString(string)
volume = self._extract_volume(dom)
return {'body': {'volume': volume}}
class VolumeController(object): class VolumeController(object):
"""The Volumes API controller for the OpenStack API.""" """The Volumes API controller for the OpenStack API."""
@@ -174,6 +214,7 @@ class VolumeController(object):
return {'volumes': res} return {'volumes': res}
@wsgi.serializers(xml=VolumeTemplate) @wsgi.serializers(xml=VolumeTemplate)
@wsgi.deserializers(xml=CreateDeserializer)
def create(self, req, body): def create(self, req, body):
"""Creates a new volume.""" """Creates a new volume."""
context = req.environ['nova.context'] context = req.environ['nova.context']

View File

@@ -311,10 +311,10 @@ class VolumeSerializerTest(test.TestCase):
elif child.tag == 'metadata': elif child.tag == 'metadata':
not_seen = set(vol['metadata'].keys()) not_seen = set(vol['metadata'].keys())
for gr_child in child: for gr_child in child:
self.assertTrue(gr_child.tag in not_seen) self.assertTrue(gr_child.get("key") in not_seen)
self.assertEqual(str(vol['metadata'][gr_child.tag]), self.assertEqual(str(vol['metadata'][gr_child.get("key")]),
gr_child.text) gr_child.text)
not_seen.remove(gr_child.tag) not_seen.remove(gr_child.get("key"))
self.assertEqual(0, len(not_seen)) self.assertEqual(0, len(not_seen))
def test_attach_show_create_serializer(self): def test_attach_show_create_serializer(self):
@@ -435,3 +435,134 @@ class VolumeSerializerTest(test.TestCase):
self.assertEqual(len(raw_volumes), len(tree)) self.assertEqual(len(raw_volumes), len(tree))
for idx, child in enumerate(tree): for idx, child in enumerate(tree):
self._verify_volume(raw_volumes[idx], child) self._verify_volume(raw_volumes[idx], child)
class TestVolumeCreateRequestXMLDeserializer(test.TestCase):
def setUp(self):
super(TestVolumeCreateRequestXMLDeserializer, self).setUp()
self.deserializer = volumes.CreateDeserializer()
def test_minimal_volume(self):
self_request = """
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
size="1"></volume>"""
request = self.deserializer.deserialize(self_request)
expected = {
"volume": {
"size": "1",
},
}
self.assertEquals(request['body'], expected)
def test_display_name(self):
self_request = """
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
size="1"
display_name="Volume-xml"></volume>"""
request = self.deserializer.deserialize(self_request)
expected = {
"volume": {
"size": "1",
"display_name": "Volume-xml",
},
}
self.assertEquals(request['body'], expected)
def test_display_description(self):
self_request = """
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
size="1"
display_name="Volume-xml"
display_description="description"></volume>"""
request = self.deserializer.deserialize(self_request)
expected = {
"volume": {
"size": "1",
"display_name": "Volume-xml",
"display_description": "description",
},
}
self.assertEquals(request['body'], expected)
def test_volume_type(self):
self_request = """
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
size="1"
display_name="Volume-xml"
display_description="description"
volume_type="289da7f8-6440-407c-9fb4-7db01ec49164"></volume>"""
request = self.deserializer.deserialize(self_request)
expected = {
"volume": {
"display_name": "Volume-xml",
"size": "1",
"display_name": "Volume-xml",
"display_description": "description",
"volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164",
},
}
self.assertEquals(request['body'], expected)
def test_availability_zone(self):
self_request = """
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
size="1"
display_name="Volume-xml"
display_description="description"
volume_type="289da7f8-6440-407c-9fb4-7db01ec49164"
availability_zone="us-east1"></volume>"""
request = self.deserializer.deserialize(self_request)
expected = {
"volume": {
"size": "1",
"display_name": "Volume-xml",
"display_description": "description",
"volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164",
"availability_zone": "us-east1",
},
}
self.assertEquals(request['body'], expected)
def test_metadata(self):
self_request = """
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
display_name="Volume-xml"
size="1">
<metadata><meta key="Type">work</meta></metadata></volume>"""
request = self.deserializer.deserialize(self_request)
expected = {
"volume": {
"display_name": "Volume-xml",
"size": "1",
"metadata": {
"Type": "work",
},
},
}
self.assertEquals(request['body'], expected)
def test_full_volume(self):
self_request = """
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
size="1"
display_name="Volume-xml"
display_description="description"
volume_type="289da7f8-6440-407c-9fb4-7db01ec49164"
availability_zone="us-east1">
<metadata><meta key="Type">work</meta></metadata></volume>"""
request = self.deserializer.deserialize(self_request)
expected = {
"volume": {
"size": "1",
"display_name": "Volume-xml",
"display_description": "description",
"volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164",
"availability_zone": "us-east1",
"metadata": {
"Type": "work",
},
},
}
self.maxDiff = None
self.assertEquals(request['body'], expected)