]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Retrieve volume image metadata using single query
authorLuis A. Garcia <luis@linux.vnet.ibm.com>
Tue, 5 Nov 2013 02:22:30 +0000 (02:22 +0000)
committerLuis A. Garcia <luis@linux.vnet.ibm.com>
Mon, 11 Nov 2013 18:51:44 +0000 (18:51 +0000)
The image metadata REST API contributions are making an individual db
call for each of the available volumes. When the number of volumes is
large the volume details call can take several minutes.

This patch changes the image metadata API contributions to take
advantage of the new db query to retrieve metadata in bulk.

Change-Id: I9a35438c1f38ea8a3d8f5b687ae58ba1f3f78121
Partial-Bug: #1197612

cinder/api/contrib/volume_image_metadata.py
cinder/tests/api/contrib/test_volume_image_metadata.py

index da5ac2f8bfada872e7c1a57643eca3b60e43fcb3..7204b29bf1094b8340bccfd2bdc1acfc13181f93 100644 (file)
 
 """The Volume Image Metadata API extension."""
 
+import logging
+
 from cinder.api import extensions
 from cinder.api.openstack import wsgi
 from cinder.api import xmlutil
 from cinder import volume
 
 
+LOG = logging.getLogger(__name__)
+
 authorize = extensions.soft_extension_authorizer('volume',
                                                  'volume_image_metadata')
 
@@ -29,16 +33,35 @@ class VolumeImageMetadataController(wsgi.Controller):
         super(VolumeImageMetadataController, self).__init__(*args, **kwargs)
         self.volume_api = volume.API()
 
-    def _add_image_metadata(self, context, resp_volume):
+    def _get_all_images_metadata(self, context):
+        """Returns the image metadata for all volumes."""
         try:
-            image_meta = self.volume_api.get_volume_image_metadata(
-                context, resp_volume)
-        except Exception:
-            return
-        else:
-            if image_meta:
-                resp_volume['volume_image_metadata'] = dict(
-                    image_meta.iteritems())
+            all_metadata = self.volume_api.get_volumes_image_metadata(context)
+        except Exception as e:
+            LOG.debug('Problem retrieving volume image metadata. '
+                      'It will be skipped. Error: %s', e)
+            all_metadata = {}
+        return all_metadata
+
+    def _add_image_metadata(self, context, resp_volume, image_meta=None):
+        """Appends the image metadata to the given volume.
+
+        :param context: the request context
+        :param resp_volume: the response volume
+        :param image_meta: The image metadata to append, if None is provided it
+                           will be retrieved from the database. An empty dict
+                           means there is no metadata and it should not be
+                           retrieved from the db.
+        """
+        if image_meta is None:
+            try:
+                image_meta = self.volume_api.get_volume_image_metadata(
+                    context, resp_volume)
+            except Exception:
+                return
+        if image_meta:
+            resp_volume['volume_image_metadata'] = dict(
+                image_meta.iteritems())
 
     @wsgi.extends
     def show(self, req, resp_obj, id):
@@ -52,8 +75,10 @@ class VolumeImageMetadataController(wsgi.Controller):
         context = req.environ['cinder.context']
         if authorize(context):
             resp_obj.attach(xml=VolumesImageMetadataTemplate())
+            all_meta = self._get_all_images_metadata(context)
             for volume in list(resp_obj.obj.get('volumes', [])):
-                self._add_image_metadata(context, volume)
+                image_meta = all_meta.get(volume['id'], {})
+                self._add_image_metadata(context, volume, image_meta)
 
 
 class Volume_image_metadata(extensions.ExtensionDescriptor):
index 8c7060de590fecff736ce9ebe1b211c76950a1a8..f702aa423d4c47e5a80244f89f501ffc9d1a9a08 100644 (file)
@@ -63,6 +63,10 @@ def fake_get_volume_image_metadata(*args, **kwargs):
     return fake_image_metadata
 
 
+def fake_get_volumes_image_metadata(*args, **kwargs):
+    return {'fake': fake_image_metadata}
+
+
 class VolumeImageMetadataTest(test.TestCase):
     content_type = 'application/json'
 
@@ -72,6 +76,8 @@ class VolumeImageMetadataTest(test.TestCase):
         self.stubs.Set(volume.API, 'get_all', fake_volume_get_all)
         self.stubs.Set(volume.API, 'get_volume_image_metadata',
                        fake_get_volume_image_metadata)
+        self.stubs.Set(volume.API, 'get_volumes_image_metadata',
+                       fake_get_volumes_image_metadata)
         self.stubs.Set(db, 'volume_get', fake_volume_get)
         self.UUID = uuid.uuid4()