diff --git a/nova/image/glance.py b/nova/image/glance.py index 970e2a4f2b89..1f2a0664e4b9 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -307,16 +307,14 @@ class GlanceImageService(object): if not _is_image_available(context, image): raise exception.ImageNotFound(image_id=image_id) - image = _translate_from_glance(image) + image = _translate_from_glance(image, + include_locations=include_locations) if include_locations: locations = image.get('locations', None) or [] du = image.get('direct_url', None) if du: locations.append({'url': du, 'metadata': {}}) image['locations'] = locations - else: - image.pop('locations', None) - image.pop('direct_url', None) return image @@ -482,8 +480,9 @@ def _translate_to_glance(image_meta): return image_meta -def _translate_from_glance(image): - image_meta = _extract_attributes(image) +def _translate_from_glance(image, include_locations=False): + image_meta = _extract_attributes(image, + include_locations=include_locations) image_meta = _convert_timestamps_to_datetimes(image_meta) image_meta = _convert_from_string(image_meta) return image_meta @@ -532,7 +531,7 @@ def _convert_to_string(metadata): return _convert(_json_dumps, metadata) -def _extract_attributes(image): +def _extract_attributes(image, include_locations=False): # NOTE(hdd): If a key is not found, base.Resource.__getattr__() may perform # a get(), resulting in a useless request back to glance. This list is # therefore sorted, with dependent attributes as the end @@ -547,6 +546,7 @@ def _extract_attributes(image): queued = getattr(image, 'status') == 'queued' queued_exclude_attrs = ['disk_format', 'container_format'] + include_locations_attrs = ['direct_url', 'locations'] output = {} for attr in IMAGE_ATTRIBUTES: @@ -560,6 +560,10 @@ def _extract_attributes(image): # NOTE(liusheng): queued image may not have these attributes and 'name' elif queued and attr in queued_exclude_attrs: output[attr] = getattr(image, attr, None) + # NOTE(mriedem): Only get location attrs if including locations. + elif attr in include_locations_attrs: + if include_locations: + output[attr] = getattr(image, attr, None) else: # NOTE(xarses): Anything that is caught with the default value # will result in a additional lookup to glance for said attr. diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py index 5334e38f48f8..394c3d1fbd16 100644 --- a/nova/tests/image/test_glance.py +++ b/nova/tests/image/test_glance.py @@ -454,7 +454,7 @@ class TestGlanceImageService(test.NoDBTestCase): self.assertEqual(same_id, image_id) self.assertEqual(service._client.host, 'something-less-likely') - def test_extracting_missing_attributes(self): + def _test_extracting_missing_attributes(self, include_locations): """Verify behavior from glance objects that are missing attributes This fakes the image class and is missing attribute as the client can @@ -475,7 +475,8 @@ class TestGlanceImageService(test.NoDBTestCase): 'updated_at': self.NOW_DATETIME, } image = MyFakeGlanceImage(metadata) - observed = glance._extract_attributes(image) + observed = glance._extract_attributes( + image, include_locations=include_locations) expected = { 'id': 1, 'name': None, @@ -492,12 +493,19 @@ class TestGlanceImageService(test.NoDBTestCase): 'deleted': None, 'status': None, 'properties': {}, - 'owner': None, - 'locations': None, - 'direct_url': None + 'owner': None } + if include_locations: + expected['locations'] = None + expected['direct_url'] = None self.assertEqual(expected, observed) + def test_extracting_missing_attributes_include_locations(self): + self._test_extracting_missing_attributes(include_locations=True) + + def test_extracting_missing_attributes_exclude_locations(self): + self._test_extracting_missing_attributes(include_locations=False) + def _create_failing_glance_client(info): class MyGlanceStubClient(glance_stubs.StubGlanceClient): @@ -652,7 +660,7 @@ class TestShow(test.NoDBTestCase): client.call.assert_called_once_with(ctx, 1, 'get', mock.sentinel.image_id) is_avail_mock.assert_called_once_with(ctx, {}) - trans_from_mock.assert_called_once_with({}) + trans_from_mock.assert_called_once_with({}, include_locations=False) self.assertIn('mock', info) self.assertEqual(mock.sentinel.trans_from, info['mock']) @@ -743,7 +751,8 @@ class TestShow(test.NoDBTestCase): client.call.assert_called_once_with(ctx, 2, 'get', image_id) avail_mock.assert_called_once_with(ctx, mock.sentinel.image) - trans_from_mock.assert_called_once_with(mock.sentinel.image) + trans_from_mock.assert_called_once_with(mock.sentinel.image, + include_locations=True) self.assertIn('locations', info) self.assertEqual(locations, info['locations'])