diff --git a/nova/image/glance.py b/nova/image/glance.py index 79255f84e1e5..a03093df6c3d 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -536,7 +536,11 @@ def _extract_attributes(image): elif queued and attr in queued_exclude_attrs: output[attr] = getattr(image, attr, None) else: - output[attr] = getattr(image, attr) + # NOTE(xarses): Anything that is caught with the default value + # will result in a additional lookup to glance for said attr. + # Notable attributes that could have this issue: + # disk_format, container_format, name, deleted, checksum + output[attr] = getattr(image, attr, None) output['properties'] = getattr(image, 'properties', {}) diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py index 2ce857b63712..35218e40f2d8 100644 --- a/nova/tests/image/test_glance.py +++ b/nova/tests/image/test_glance.py @@ -572,6 +572,48 @@ class TestGlanceImageService(test.NoDBTestCase): self.assertEqual(same_id, image_id) self.assertEqual(service._client.host, 'something-less-likely') + def test_extracting_missing_attributes(self): + """Verify behavior from glance objects that are missing attributes + + This fakes the image class and is missing attribute as the client can + return if they're not set in the database. + """ + class MyFakeGlanceImage(glance_stubs.FakeImage): + def __init__(self, metadata): + IMAGE_ATTRIBUTES = ['size', 'owner', 'id', 'created_at', + 'updated_at', 'status', 'min_disk', + 'min_ram', 'is_public'] + raw = dict.fromkeys(IMAGE_ATTRIBUTES) + raw.update(metadata) + self.__dict__['raw'] = raw + + metadata = { + 'id': 1, + 'created_at': self.NOW_DATETIME, + 'updated_at': self.NOW_DATETIME, + } + image = MyFakeGlanceImage(metadata) + observed = glance._extract_attributes(image) + expected = { + 'id': 1, + 'name': None, + 'is_public': None, + 'size': None, + 'min_disk': None, + 'min_ram': None, + 'disk_format': None, + 'container_format': None, + 'checksum': None, + 'created_at': self.NOW_DATETIME, + 'updated_at': self.NOW_DATETIME, + 'deleted_at': None, + 'deleted': None, + 'status': None, + 'properties': {}, + 'owner': None, + } + self.assertEqual(expected, observed) + def _create_failing_glance_client(info): class MyGlanceStubClient(glance_stubs.StubGlanceClient):