From 652a1a8b33246beb5fa18692f614274c2a03b841 Mon Sep 17 00:00:00 2001 From: Dmitriy Uvarenkov Date: Thu, 18 Aug 2016 15:33:23 +0300 Subject: [PATCH] Add v2 glance image props Added properties, supported by glanceclient v2: architecture kernel_id os_distro ramdisk_id and also owner property, supported by glanceclient v1. Closes-Bug: #1606150 Change-Id: I15b9295f631615c1d98d7872fb973d5e8ca226da --- .../resources/openstack/glance/image.py | 77 +++++++++++++++++-- heat/tests/openstack/glance/test_image.py | 56 +++++++++++--- 2 files changed, 116 insertions(+), 17 deletions(-) diff --git a/heat/engine/resources/openstack/glance/image.py b/heat/engine/resources/openstack/glance/image.py index 4c307612af..ec88911bf4 100644 --- a/heat/engine/resources/openstack/glance/image.py +++ b/heat/engine/resources/openstack/glance/image.py @@ -30,13 +30,18 @@ class GlanceImage(resource.Resource): PROPERTIES = ( NAME, IMAGE_ID, IS_PUBLIC, MIN_DISK, MIN_RAM, PROTECTED, - DISK_FORMAT, CONTAINER_FORMAT, LOCATION, TAGS, EXTRA_PROPERTIES + DISK_FORMAT, CONTAINER_FORMAT, LOCATION, TAGS, EXTRA_PROPERTIES, + ARCHITECTURE, KERNEL_ID, OS_DISTRO, OWNER, RAMDISK_ID ) = ( 'name', 'id', 'is_public', 'min_disk', 'min_ram', 'protected', 'disk_format', 'container_format', 'location', 'tags', - 'extra_properties' + 'extra_properties', 'architecture', 'kernel_id', 'os_distro', + 'owner', 'ramdisk_id' ) + glance_id_pattern = ('^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}' + '-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$') + properties_schema = { NAME: properties.Schema( properties.Schema.STRING, @@ -118,6 +123,45 @@ class GlanceImage(resource.Resource): update_allowed=True, default={}, support_status=support.SupportStatus(version='7.0.0') + ), + ARCHITECTURE: properties.Schema( + properties.Schema.STRING, + _('Operating system architecture.'), + update_allowed=True, + support_status=support.SupportStatus(version='7.0.0') + ), + KERNEL_ID: properties.Schema( + properties.Schema.STRING, + _('ID of image stored in Glance that should be used as ' + 'the kernel when booting an AMI-style image.'), + update_allowed=True, + support_status=support.SupportStatus(version='7.0.0'), + constraints=[ + constraints.AllowedPattern(glance_id_pattern) + ] + ), + OS_DISTRO: properties.Schema( + properties.Schema.STRING, + _('The common name of the operating system distribution ' + 'in lowercase.'), + update_allowed=True, + support_status=support.SupportStatus(version='7.0.0') + ), + OWNER: properties.Schema( + properties.Schema.STRING, + _('Owner of the image.'), + update_allowed=True, + support_status=support.SupportStatus(version='7.0.0') + ), + RAMDISK_ID: properties.Schema( + properties.Schema.STRING, + _('ID of image stored in Glance that should be used as ' + 'the ramdisk when booting an AMI-style image.'), + update_allowed=True, + support_status=support.SupportStatus(version='7.0.0'), + constraints=[ + constraints.AllowedPattern(glance_id_pattern) + ] ) } @@ -131,9 +175,24 @@ class GlanceImage(resource.Resource): tags = args.pop(self.TAGS, []) args['properties'] = args.pop(self.EXTRA_PROPERTIES, {}) + architecture = args.pop(self.ARCHITECTURE, None) + kernel_id = args.pop(self.KERNEL_ID, None) + os_distro = args.pop(self.OS_DISTRO, None) + ramdisk_id = args.pop(self.RAMDISK_ID, None) + image_id = self.client().images.create(**args).id self.resource_id_set(image_id) + v2_images = self.client(version=self.client_plugin().V2).images + if architecture is not None: + v2_images.update(image_id, architecture=architecture) + if kernel_id is not None: + v2_images.update(image_id, kernel_id=kernel_id) + if os_distro is not None: + v2_images.update(image_id, os_distro=os_distro) + if ramdisk_id is not None: + v2_images.update(image_id, ramdisk_id=ramdisk_id) + for tag in tags: self.client( version=self.client_plugin().V2).image_tags.update( @@ -149,7 +208,7 @@ class GlanceImage(resource.Resource): def handle_update(self, json_snippet, tmpl_diff, prop_diff): if prop_diff and self.TAGS in prop_diff: existing_tags = self.properties.get(self.TAGS) or [] - diff_tags = prop_diff[self.TAGS] or [] + diff_tags = prop_diff.pop(self.TAGS) or [] new_tags = set(diff_tags) - set(existing_tags) for tag in new_tags: @@ -166,18 +225,20 @@ class GlanceImage(resource.Resource): self.resource_id, tag) + v2_images = self.client(version=self.client_plugin().V2).images + if self.EXTRA_PROPERTIES in prop_diff: old_properties = self.properties.get(self.EXTRA_PROPERTIES) or {} - new_properties = prop_diff[self.EXTRA_PROPERTIES] + new_properties = prop_diff.pop(self.EXTRA_PROPERTIES) + prop_diff.update(new_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) + v2_images.update(self.resource_id, remove_props, **prop_diff) + else: + v2_images.update(self.resource_id, **prop_diff) def _show_resource(self): if self.glance().version == 1.0: diff --git a/heat/tests/openstack/glance/test_image.py b/heat/tests/openstack/glance/test_image.py index e02ded5311..f354f522af 100644 --- a/heat/tests/openstack/glance/test_image.py +++ b/heat/tests/openstack/glance/test_image.py @@ -38,6 +38,12 @@ resources: min_ram: 512 protected: False location: https://launchpad.net/cirros/cirros-0.3.0-x86_64-disk.img + location: https://launchpad.net/cirros/cirros-0.3.0-x86_64-disk.img + architecture: test_architecture + kernel_id: 12345678-1234-1234-1234-123456789012 + os_distro: test_distro + owner: test_owner + ramdisk_id: 12345678-1234-1234-1234-123456789012 ''' image_template_validate = ''' @@ -231,11 +237,22 @@ class GlanceImageTest(common.HeatTestCase): min_ram=512, name=u'cirros_image', protected=False, + owner=u'test_owner', properties={} ) self.image_tags.update.assert_called_once_with( self.my_image.resource_id, 'tag1') + calls = [mock.call(self.my_image.resource_id, + architecture='test_architecture'), + mock.call(self.my_image.resource_id, + kernel_id='12345678-1234-1234-1234-123456789012'), + mock.call(self.my_image.resource_id, + os_distro='test_distro'), + mock.call(self.my_image.resource_id, + ramdisk_id='12345678-1234-1234-1234-123456789012'), + ] + self.images.update.assert_has_calls(calls) def _handle_update_tags(self, prop_diff): self.my_image.handle_update(json_snippet=None, @@ -251,17 +268,31 @@ class GlanceImageTest(common.HeatTestCase): 'tag1' ) - def _handle_update_properties(self, prop_diff): + def test_image_handle_update(self): + self.my_image.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' + prop_diff = { + 'architecture': 'test_architecture', + 'kernel_id': '12345678-1234-1234-1234-123456789012', + 'os_distro': 'test_distro', + 'owner': 'test_owner', + '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, [], - **prop_diff['extra_properties']) + architecture='test_architecture', + kernel_id='12345678-1234-1234-1234-123456789012', + os_distro='test_distro', + owner='test_owner', + ramdisk_id='12345678-1234-1234-1234-123456789012', + key1='value1', key2='value2' + ) - def test_image_handle_update(self): + def test_image_handle_update_tags(self): self.my_image.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' self.my_image.t['Properties']['tags'] = ['tag1'] @@ -269,9 +300,6 @@ 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' @@ -339,7 +367,12 @@ class GlanceImageTest(common.HeatTestCase): 'min_ram': 0, 'id': '41f0e60c-ebb4-4375-a2b4-845ae8b9c995', 'tags': [], - 'extra_properties': {} + 'extra_properties': {}, + 'architecture': 'test_architecture', + 'kernel_id': '12345678-1234-1234-1234-123456789012', + 'os_distro': 'test_distro', + 'owner': 'test_owner', + 'ramdisk_id': '12345678-1234-1234-1234-123456789012' } if version == 1.0: image = mock.MagicMock() @@ -363,7 +396,12 @@ class GlanceImageTest(common.HeatTestCase): 'min_ram': 0, 'id': '41f0e60c-ebb4-4375-a2b4-845ae8b9c995', 'tags': [], - 'extra_properties': {} + 'extra_properties': {}, + 'architecture': 'test_architecture', + 'kernel_id': '12345678-1234-1234-1234-123456789012', + 'os_distro': 'test_distro', + 'owner': 'test_owner', + 'ramdisk_id': '12345678-1234-1234-1234-123456789012' } if version == 1.0: expected.update({'location': 'stub'})