diff --git a/heat/engine/resources/openstack/glance/image.py b/heat/engine/resources/openstack/glance/image.py index 2f0053faa3..1ac95d2e0c 100644 --- a/heat/engine/resources/openstack/glance/image.py +++ b/heat/engine/resources/openstack/glance/image.py @@ -29,14 +29,15 @@ class GlanceWebImage(resource.Resource): PROPERTIES = ( NAME, IMAGE_ID, MIN_DISK, MIN_RAM, PROTECTED, - DISK_FORMAT, CONTAINER_FORMAT, LOCATION, TAGS, + DISK_FORMAT, CONTAINER_FORMAT, LOCATION, TAGS, EXTRA_PROPERTIES, ARCHITECTURE, KERNEL_ID, OS_DISTRO, OS_VERSION, OWNER, VISIBILITY, RAMDISK_ID ) = ( 'name', 'id', 'min_disk', 'min_ram', 'protected', 'disk_format', 'container_format', 'location', 'tags', - 'architecture', 'kernel_id', 'os_distro', 'os_version', 'owner', - 'visibility', 'ramdisk_id' + 'extra_properties', 'architecture', 'kernel_id', + 'os_distro', 'os_version', 'owner', 'visibility', + 'ramdisk_id' ) glance_id_pattern = ('^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}' @@ -108,6 +109,12 @@ class GlanceWebImage(resource.Resource): _('List of image tags.'), update_allowed=True, ), + EXTRA_PROPERTIES: properties.Schema( + properties.Schema.MAP, + _('Arbitrary properties to associate with the image.'), + update_allowed=True, + default={} + ), ARCHITECTURE: properties.Schema( properties.Schema.STRING, _('Operating system architecture.'), @@ -168,6 +175,11 @@ class GlanceWebImage(resource.Resource): if v is not None) location = args.pop(self.LOCATION) + extra_properties = args.pop(self.EXTRA_PROPERTIES, {}) + if extra_properties: + for k, v in extra_properties.items(): + if k not in self.properties: + args[k] = v images = self.client().images image_id = images.create( **args).id @@ -201,7 +213,17 @@ class GlanceWebImage(resource.Resource): images = self.client().images - images.update(self.resource_id, **prop_diff) + # 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 + remove_props = [] + if self.EXTRA_PROPERTIES in prop_diff: + old_properties = self.properties.get(self.EXTRA_PROPERTIES) or {} + new_properties = prop_diff.pop(self.EXTRA_PROPERTIES) + prop_diff.update(new_properties) + remove_props = list(set(old_properties) - set(new_properties)) + + images.update(self.resource_id, remove_props, **prop_diff) def validate(self): super(GlanceWebImage, self).validate() diff --git a/heat/tests/openstack/glance/test_image.py b/heat/tests/openstack/glance/test_image.py index 73f47a81a0..b38b66ccee 100644 --- a/heat/tests/openstack/glance/test_image.py +++ b/heat/tests/openstack/glance/test_image.py @@ -640,7 +640,7 @@ class GlanceWebImageTest(common.HeatTestCase): name=u'cirros_image', protected=False, owner=u'test_owner', - tags=['tag1'], + tags=['tag1'] ) def _handle_update_tags(self, prop_diff): @@ -664,18 +664,21 @@ class GlanceWebImageTest(common.HeatTestCase): 'kernel_id': '12345678-1234-1234-1234-123456789012', 'os_distro': 'test_distro', 'owner': 'test_owner', - 'ramdisk_id': '12345678-1234-1234-1234-123456789012'} + 'ramdisk_id': '12345678-1234-1234-1234-123456789012', + 'extra_properties': {'key1': 'value1', 'key2': 'value2'}} 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, + [], architecture='test_architecture', kernel_id='12345678-1234-1234-1234-123456789012', os_distro='test_distro', owner='test_owner', - ramdisk_id='12345678-1234-1234-1234-123456789012' + ramdisk_id='12345678-1234-1234-1234-123456789012', + key1='value1', key2='value2' ) def test_image_handle_update_tags(self): @@ -756,6 +759,7 @@ class GlanceWebImageTest(common.HeatTestCase): 'min_ram': 0, 'id': '41f0e60c-ebb4-4375-a2b4-845ae8b9c995', 'tags': [], + 'extra_properties': {}, 'architecture': 'test_architecture', 'kernel_id': '12345678-1234-1234-1234-123456789012', 'os_distro': 'test_distro', @@ -778,6 +782,7 @@ class GlanceWebImageTest(common.HeatTestCase): 'min_ram': 0, 'id': '41f0e60c-ebb4-4375-a2b4-845ae8b9c995', 'tags': [], + 'extra_properties': {}, 'architecture': 'test_architecture', 'kernel_id': '12345678-1234-1234-1234-123456789012', 'os_distro': 'test_distro',