diff --git a/nova/image/glance.py b/nova/image/glance.py index f12c0ca546b0..a90647b2f2fa 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -965,7 +965,9 @@ def _extract_attributes(image, include_locations=False): output[attr] = getattr(image, attr, None) # NOTE(mdorman): 'size' attribute must not be 'None', so use 0 instead elif attr == 'size': - output[attr] = getattr(image, attr) or 0 + # NOTE(mriedem): A snapshot image may not have the size attribute + # set so default to 0. + output[attr] = getattr(image, attr, 0) or 0 else: # NOTE(xarses): Anything that is caught with the default value # will result in an additional lookup to glance for said attr. diff --git a/nova/tests/unit/image/test_glance.py b/nova/tests/unit/image/test_glance.py index e0c8391f562a..6158c48903ae 100644 --- a/nova/tests/unit/image/test_glance.py +++ b/nova/tests/unit/image/test_glance.py @@ -2412,6 +2412,25 @@ class TestExtractAttributes(test.NoDBTestCase): image_v2, include_locations=False) self.assertEqual(v1_output, v2_output) + @mock.patch.object(schemas, 'Schema', side_effect=FakeSchema) + def test_extract_image_attributes_empty_images_no_size(self, + mocked_schema): + image_v1_dict = dict(image_fixtures['empty_image_v1']) + # pop the size attribute since it might not be set on a snapshot image + image_v1_dict.pop('size') + image_v2 = ImageV2(image_fixtures['empty_image_v2']) + + image_v1 = collections.namedtuple('_', image_v1_dict.keys())( + **image_v1_dict) + + self.flags(use_glance_v1=True, group='glance') + v1_output = glance._translate_from_glance( + image_v1, include_locations=False) + self.flags(use_glance_v1=False, group='glance') + v2_output = glance._translate_from_glance( + image_v2, include_locations=False) + self.assertEqual(v1_output, v2_output) + @mock.patch.object(schemas, 'Schema', side_effect=FakeSchema) def test_extract_image_attributes_active_images_custom_prop( self, mocked_schema):