From 8600394ec4dd4f800d774e4ed0c24fe087d8e1d3 Mon Sep 17 00:00:00 2001 From: Eoghan Glynn Date: Mon, 9 Jul 2012 11:59:56 +0000 Subject: Support EC2 CreateImage API for boot-from-volume Fixes bug lp 988335 For fidelity with AWS, where the EC2 CreateImage API is only supported for EBS-backed instances, we now support this API for booted-from-volume nova servers. We create a "place-holder" image in glance with the image data being effectively empty, and the following properties set: * the imaged instance's kernel and ramdisk IDs * block device mapping containing the appropriate snapshot ID(s) so that we can boot from this image without providing additional context (such as via the nova boot --block-device-mapping option) Change-Id: I0b3d18d7922f2ad1bc687fa88e2f5d4cf5aa068b --- nova/api/ec2/cloud.py | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 94302383c..af74e75b8 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -1449,6 +1449,7 @@ class CloudController(object): # NOTE(yamahata): name/description are ignored by register_image(), # do so here no_reboot = kwargs.get('no_reboot', False) + name = kwargs.get('name') validate_ec2_id(instance_id) ec2_instance_id = instance_id instance_id = ec2utils.ec2_id_to_id(ec2_instance_id) @@ -1481,14 +1482,22 @@ class CloudController(object): raise exception.EC2APIError( _('Couldn\'t stop instance with in %d sec') % timeout) - src_image = self._get_image(context, instance['image_ref']) - properties = src_image['properties'] + glance_uuid = instance['image_ref'] + ec2_image_id = ec2utils.glance_id_to_ec2_id(context, glance_uuid) + src_image = self._get_image(context, ec2_image_id) + new_image = dict(src_image) + properties = new_image['properties'] if instance['root_device_name']: properties['root_device_name'] = instance['root_device_name'] + # meaningful image name + name_map = dict(instance=instance['uuid'], now=timeutils.isotime()) + new_image['name'] = (name or + _('image of %(instance)s at %(now)s') % name_map) + mapping = [] bdms = db.block_device_mapping_get_all_by_instance(context, - instance_id) + instance['uuid']) for bdm in bdms: if bdm.no_device: continue @@ -1501,15 +1510,16 @@ class CloudController(object): m[attr] = val volume_id = m.get('volume_id') - if m.get('snapshot_id') and volume_id: + snapshot_id = m.get('snapshot_id') + if snapshot_id and volume_id: # create snapshot based on volume_id volume = self.volume_api.get(context, volume_id) # NOTE(yamahata): Should we wait for snapshot creation? # Linux LVM snapshot creation completes in # short time, it doesn't matter for now. + name = _('snapshot for %s') % new_image['name'] snapshot = self.volume_api.create_snapshot_force( - context, volume, volume['display_name'], - volume['display_description']) + context, volume, name, volume['display_description']) m['snapshot_id'] = snapshot['id'] del m['volume_id'] @@ -1536,14 +1546,30 @@ class CloudController(object): properties['block_device_mapping'] = mapping for attr in ('status', 'location', 'id'): - src_image.pop(attr, None) + new_image.pop(attr, None) + + # the new image is simply a bucket of properties (particularly the + # block device mapping, kernel and ramdisk IDs) with no image data, + # hence the zero size + new_image['size'] = 0 + + def _unmap_id_property(properties, name): + if properties[name]: + properties[name] = ec2utils.id_to_glance_id(context, + properties[name]) - image_id = self._register_image(context, src_image) + # ensure the ID properties are unmapped back to the glance UUID + _unmap_id_property(properties, 'kernel_id') + _unmap_id_property(properties, 'ramdisk_id') + + new_image = self.image_service.service.create(context, new_image, + data='') + ec2_id = ec2utils.glance_id_to_ec2_id(context, new_image['id']) if restart_instance: self.compute_api.start(context, instance_id=instance_id) - return {'imageId': image_id} + return {'imageId': ec2_id} class CloudSecurityGroupAPI(compute.api.SecurityGroupAPI): -- cgit