From 56e5805203a14acad78dc806247b5f0f4a22c9f8 Mon Sep 17 00:00:00 2001 From: "Luis A. Garcia" Date: Tue, 5 Nov 2013 02:22:30 +0000 Subject: [PATCH] Retrieve volume image metadata using single query 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 | 45 ++++++++++++++----- .../api/contrib/test_volume_image_metadata.py | 6 +++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/cinder/api/contrib/volume_image_metadata.py b/cinder/api/contrib/volume_image_metadata.py index da5ac2f8bf..7204b29bf1 100644 --- a/cinder/api/contrib/volume_image_metadata.py +++ b/cinder/api/contrib/volume_image_metadata.py @@ -14,12 +14,16 @@ """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 @@ def __init__(self, *args, **kwargs): 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 @@ def detail(self, req, resp_obj): 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): diff --git a/cinder/tests/api/contrib/test_volume_image_metadata.py b/cinder/tests/api/contrib/test_volume_image_metadata.py index 8c7060de59..f702aa423d 100644 --- a/cinder/tests/api/contrib/test_volume_image_metadata.py +++ b/cinder/tests/api/contrib/test_volume_image_metadata.py @@ -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 @@ def setUp(self): 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()