From a39fa8fb2876a98b83570295504c0f5e46da7d6a Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Tue, 20 Feb 2024 14:37:25 +0530 Subject: [PATCH] Add support for glance new location APIs This patch adds the call for ``add_image_location`` API which triggers the new location API workflow in glance that addresses OSSN-0065. It is more secure and robust compared to the old location workflow. This call will be made when glance is using cinder as a backend and we want to perform an optimized ``upload volume to image`` operation. Signed-off-by: Rajat Dhasmana Change-Id: Ib70219a9d085257c90a75ddcfcb935b4659fd28d --- cinder/image/glance.py | 25 +++++++++++++++++++----- cinder/tests/unit/image/test_glance.py | 27 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/cinder/image/glance.py b/cinder/image/glance.py index 9002afb0973..bdcc91d4b4b 100644 --- a/cinder/image/glance.py +++ b/cinder/image/glance.py @@ -377,11 +377,26 @@ class GlanceImageService(object): Returns a dict containing image metadata on success. """ client = GlanceClientWrapper() - try: - return client.call(context, 'add_location', - image_id, url, metadata) - except Exception: - _reraise_translated_image_exception(image_id) + # The ``add_image_location`` API was added to address + # OSSN-0065, however to keep backward compatibility, + # we need to try with the old ``add_location`` call + # if we are using an older version of glance. + # TODO: Remove the ``add_location`` API call when 2024.1 + # trasitions to unmaintained. (``add_image_location`` + # was added in 2024.2). + try_methods = ('add_image_location', 'add_location') + for method in try_methods: + try: + return client.call(context, method, + image_id, url, metadata) + except glanceclient.exc.HTTPNotImplemented: + LOG.debug('Glance method %s not available', method) + except Exception: + _reraise_translated_image_exception(image_id) + # If both method return HTTPNotImplemented exception + raise exception.ProgrammingError( + reason='unwarranted assumption about available glanceclient ' + 'methods.') def download(self, context: context.RequestContext, diff --git a/cinder/tests/unit/image/test_glance.py b/cinder/tests/unit/image/test_glance.py index b04d59b5475..9f4d09ac9cc 100644 --- a/cinder/tests/unit/image/test_glance.py +++ b/cinder/tests/unit/image/test_glance.py @@ -773,6 +773,33 @@ class TestGlanceImageService(test.TestCase): self.assertEqual(self.NOW_DATETIME, image_meta['created_at']) self.assertEqual(self.NOW_DATETIME, image_meta['updated_at']) + @mock.patch.object(glance.GlanceClientWrapper, 'call') + def test_add_location(self, mock_call): + image_id = mock.sentinel.image_id + service = glance.GlanceImageService(client=mock_call) + url = 'cinder://fake-store/c984be2b-8789-4b9e-bf71-19164f537e63' + metadata = {'store': 'fake-store'} + + service.add_location(self.context, image_id, url, metadata) + mock_call.assert_called_once_with( + self.context, 'add_image_location', image_id, url, metadata) + + @mock.patch.object(glance.GlanceClientWrapper, 'call') + def test_add_location_old(self, mock_call): + mock_call.side_effect = [glanceclient.exc.HTTPNotImplemented, None] + image_id = mock.sentinel.image_id + service = glance.GlanceImageService(client=mock_call) + url = 'cinder://fake-store/c984be2b-8789-4b9e-bf71-19164f537e63' + metadata = {'store': 'fake-store'} + + service.add_location(self.context, image_id, url, metadata) + calls = [ + mock.call.call( + self.context, 'add_image_location', image_id, url, metadata), + mock.call.call( + self.context, 'add_location', image_id, url, metadata)] + mock_call.assert_has_calls(calls) + def test_download_with_retries(self): tries = [0]