diff --git a/nova/image/glance.py b/nova/image/glance.py index cb5633fe68a2..92417feafa8e 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -563,11 +563,19 @@ class GlanceImageServiceV2(object): def _add_location(self, context, image_id, location): # 'show_multiple_locations' must be enabled in glance api conf file. - try: - return self._client.call( - context, 2, 'add_location', args=(image_id, location, {})) - except glanceclient.exc.HTTPBadRequest: - _reraise_translated_exception() + try_methods = ('add_image_location', 'add_location') + exc = None + for method in try_methods: + try: + return self._client.call( + context, 2, method, args=(image_id, location, {})) + except glanceclient.exc.HTTPNotImplemented as e: + exc = e + LOG.debug('Glance method %s not available', method) + except glanceclient.exc.HTTPBadRequest as e: + exc = e + _reraise_translated_exception() + raise exc def _add_image_member(self, context, image_id, member_id): """Grant access to another project that does not own the image @@ -666,7 +674,7 @@ class GlanceImageServiceV2(object): # Sending image location in a separate request. if location: - image = self._add_location(context, image_id, location) + self._add_location(context, image_id, location) # Add image membership in a separate request. if sharing_member_id: @@ -715,7 +723,7 @@ class GlanceImageServiceV2(object): # Sending image location in a separate request. if location: - image = self._add_location(context, image_id, location) + self._add_location(context, image_id, location) # If we have some data we have to send it in separate request and # update the image then. diff --git a/nova/tests/unit/image/test_glance.py b/nova/tests/unit/image/test_glance.py index a7051a733fa7..18d06ab9506f 100644 --- a/nova/tests/unit/image/test_glance.py +++ b/nova/tests/unit/image/test_glance.py @@ -1745,7 +1745,8 @@ class TestCreate(test.NoDBTestCase): @mock.patch('nova.image.glance._translate_from_glance') @mock.patch('nova.image.glance._translate_to_glance') def test_create_success_v2_with_location( - self, trans_to_mock, trans_from_mock): + self, trans_to_mock, trans_from_mock, old_api=False, + new_api=True): translated = { 'id': mock.sentinel.id, 'name': mock.sentinel.name, @@ -1755,15 +1756,40 @@ class TestCreate(test.NoDBTestCase): trans_from_mock.return_value = mock.sentinel.trans_from image_mock = {} client = mock.MagicMock() - client.call.return_value = translated + if old_api: + client.call.side_effect = [translated, + glanceclient.exc.HTTPNotImplemented, + None] + elif not new_api: + # If neither API is available we expect back whatever glanceclient + # raised to us + client.call.side_effect = test.TestingException + else: + client.call.side_effect = [translated, None] ctx = mock.sentinel.ctx service = glance.GlanceImageServiceV2(client) + if not new_api and not old_api: + self.assertRaises(test.TestingException, + service.create, ctx, image_mock) + return image_meta = service.create(ctx, image_mock) trans_to_mock.assert_called_once_with(image_mock) - self.assertEqual(2, client.call.call_count) + calls = [c[0][2] for c in client.call.call_args_list] + if old_api: + expected = ['create', 'add_image_location', 'add_location'] + else: + expected = ['create', 'add_image_location'] + self.assertEqual(expected, calls) trans_from_mock.assert_called_once_with(translated) self.assertEqual(mock.sentinel.trans_from, image_meta) + def test_create_success_v2_with_location_old_api(self): + self.test_create_success_v2_with_location(old_api=True) + + def test_create_success_v2_with_location_no_api(self): + self.test_create_success_v2_with_location(old_api=False, + new_api=False) + @mock.patch('nova.image.glance._translate_from_glance') @mock.patch('nova.image.glance._translate_to_glance') def test_create_success_v2_with_sharing( diff --git a/requirements.txt b/requirements.txt index fb751b99479b..2a62a4b3367a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,7 +24,7 @@ jsonschema>=4.0.0 # MIT python-cinderclient>=4.0.1 # Apache-2.0 keystoneauth1>=3.16.0 # Apache-2.0 python-neutronclient>=7.1.0 # Apache-2.0 -python-glanceclient>=2.8.0 # Apache-2.0 +python-glanceclient>=4.7.0 # Apache-2.0 requests>=2.25.1 # Apache-2.0 stevedore>=1.20.0 # Apache-2.0 websockify>=0.9.0 # LGPLv3