From c3476b5ca7ab5237d3cb8a84fcb7a3292237b764 Mon Sep 17 00:00:00 2001 From: Eoghan Glynn Date: Fri, 7 Sep 2012 12:45:27 +0000 Subject: Create image of volume-backed instance via native API Fixes bug 1034730. Avoids 'qemu-img snapshot' failure when native API create_image action is applied to a volume-backed instance. Applies the same logic as is used to create a placeholder image from a volume-backed instance via the EC2 API CreateImage operation, with the now-common code refactored into the ComputeAPI class. Change-Id: I624584ae9adbf30629f0e814d340da6b9e6e59bd --- nova/api/ec2/cloud.py | 81 ++++++----------------------------- nova/api/openstack/compute/servers.py | 25 ++++++++--- 2 files changed, 32 insertions(+), 74 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 14aa39438..e26813b99 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -1538,71 +1538,7 @@ class CloudController(object): 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 = [] - for bdm in bdms: - if bdm.no_device: - continue - m = {} - for attr in ('device_name', 'snapshot_id', 'volume_id', - 'volume_size', 'delete_on_termination', 'no_device', - 'virtual_name'): - val = getattr(bdm, attr) - if val is not None: - m[attr] = val - - volume_id = m.get('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, name, volume['display_description']) - m['snapshot_id'] = snapshot['id'] - del m['volume_id'] - - if m: - mapping.append(m) - - for m in _properties_get_mappings(properties): - virtual_name = m['virtual'] - if virtual_name in ('ami', 'root'): - continue - - assert block_device.is_swap_or_ephemeral(virtual_name) - device_name = m['device'] - if device_name in [b['device_name'] for b in mapping - if not b.get('no_device', False)]: - continue - - # NOTE(yamahata): swap and ephemeral devices are specified in - # AMI, but disabled for this instance by user. - # So disable those device by no_device. - mapping.append({'device_name': device_name, 'no_device': True}) - - if mapping: - properties['block_device_mapping'] = mapping - - for attr in ('status', 'location', 'id'): - 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 + image_meta = dict(src_image) def _unmap_id_property(properties, name): if properties[name]: @@ -1610,11 +1546,18 @@ class CloudController(object): properties[name]) # ensure the ID properties are unmapped back to the glance UUID - _unmap_id_property(properties, 'kernel_id') - _unmap_id_property(properties, 'ramdisk_id') + _unmap_id_property(image_meta['properties'], 'kernel_id') + _unmap_id_property(image_meta['properties'], 'ramdisk_id') + + # meaningful image name + name_map = dict(instance=instance['uuid'], now=timeutils.isotime()) + name = name or _('image of %(instance)s at %(now)s') % name_map + + new_image = self.compute_api.snapshot_volume_backed(context, + instance, + image_meta, + name) - 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: diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index 9f462c565..fc4905a11 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -1173,14 +1173,29 @@ class Controller(wsgi.Controller): instance = self._get_server(context, req, id) + bdms = self.compute_api.get_instance_bdms(context, instance) + try: - image = self.compute_api.snapshot(context, - instance, - image_name, - extra_properties=props) + if self.compute_api.is_volume_backed_instance(context, instance, + bdms): + img = instance['image_ref'] + src_image = self.compute_api.image_service.show(context, img) + image_meta = dict(src_image) + + image = self.compute_api.snapshot_volume_backed( + context, + instance, + image_meta, + image_name, + extra_properties=props) + else: + image = self.compute_api.snapshot(context, + instance, + image_name, + extra_properties=props) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, - 'createImage') + 'createImage') # build location of newly-created image entity image_id = str(image['id']) -- cgit