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:
		@@ -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']
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user