diff --git a/heat/engine/resources/openstack/glance/image.py b/heat/engine/resources/openstack/glance/image.py index a361525588..4c307612af 100644 --- a/heat/engine/resources/openstack/glance/image.py +++ b/heat/engine/resources/openstack/glance/image.py @@ -30,10 +30,11 @@ class GlanceImage(resource.Resource): PROPERTIES = ( NAME, IMAGE_ID, IS_PUBLIC, MIN_DISK, MIN_RAM, PROTECTED, - DISK_FORMAT, CONTAINER_FORMAT, LOCATION, TAGS + DISK_FORMAT, CONTAINER_FORMAT, LOCATION, TAGS, EXTRA_PROPERTIES ) = ( 'name', 'id', 'is_public', 'min_disk', 'min_ram', 'protected', - 'disk_format', 'container_format', 'location', 'tags' + 'disk_format', 'container_format', 'location', 'tags', + 'extra_properties' ) properties_schema = { @@ -110,6 +111,13 @@ class GlanceImage(resource.Resource): _('List of image tags.'), update_allowed=True, support_status=support.SupportStatus(version='7.0.0') + ), + EXTRA_PROPERTIES: properties.Schema( + properties.Schema.MAP, + _('Arbitrary properties to associate with the image.'), + update_allowed=True, + default={}, + support_status=support.SupportStatus(version='7.0.0') ) } @@ -122,6 +130,7 @@ class GlanceImage(resource.Resource): if v is not None) tags = args.pop(self.TAGS, []) + args['properties'] = args.pop(self.EXTRA_PROPERTIES, {}) image_id = self.client().images.create(**args).id self.resource_id_set(image_id) @@ -157,6 +166,19 @@ class GlanceImage(resource.Resource): self.resource_id, tag) + if self.EXTRA_PROPERTIES in prop_diff: + old_properties = self.properties.get(self.EXTRA_PROPERTIES) or {} + new_properties = prop_diff[self.EXTRA_PROPERTIES] + remove_props = list(set(old_properties) - set(new_properties)) + + # Though remove_props defaults to None within the glanceclient, + # setting it to a list (possibly []) every time ensures only one + # calling format to images.update + self.client(version=self.client_plugin().V2).images.update( + self.resource_id, + remove_props, + **new_properties) + def _show_resource(self): if self.glance().version == 1.0: return super(GlanceImage, self)._show_resource() diff --git a/heat/tests/openstack/glance/test_image.py b/heat/tests/openstack/glance/test_image.py index 0262982745..e02ded5311 100644 --- a/heat/tests/openstack/glance/test_image.py +++ b/heat/tests/openstack/glance/test_image.py @@ -230,7 +230,8 @@ class GlanceImageTest(common.HeatTestCase): min_disk=10, min_ram=512, name=u'cirros_image', - protected=False + protected=False, + properties={} ) self.image_tags.update.assert_called_once_with( self.my_image.resource_id, @@ -250,6 +251,16 @@ class GlanceImageTest(common.HeatTestCase): 'tag1' ) + def _handle_update_properties(self, prop_diff): + self.my_image.handle_update(json_snippet=None, + tmpl_diff=None, + prop_diff=prop_diff) + + self.images.update.assert_called_once_with( + self.my_image.resource_id, + [], + **prop_diff['extra_properties']) + def test_image_handle_update(self): self.my_image.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' @@ -258,6 +269,9 @@ class GlanceImageTest(common.HeatTestCase): self._handle_update_tags(prop_diff) + prop_diff = {'extra_properties': {'key1': 'value1', 'key2': 'value2'}} + self._handle_update_properties(prop_diff) + def test_image_handle_update_remove_tags(self): self.my_image.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' @@ -324,7 +338,8 @@ class GlanceImageTest(common.HeatTestCase): 'min_disk': 0, 'min_ram': 0, 'id': '41f0e60c-ebb4-4375-a2b4-845ae8b9c995', - 'tags': [] + 'tags': [], + 'extra_properties': {} } if version == 1.0: image = mock.MagicMock() @@ -347,7 +362,8 @@ class GlanceImageTest(common.HeatTestCase): 'min_disk': 0, 'min_ram': 0, 'id': '41f0e60c-ebb4-4375-a2b4-845ae8b9c995', - 'tags': [] + 'tags': [], + 'extra_properties': {} } if version == 1.0: expected.update({'location': 'stub'})