summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorEoghan Glynn <eglynn@redhat.com>2012-09-07 12:45:27 +0000
committerEoghan Glynn <eglynn@redhat.com>2012-09-09 13:00:24 +0100
commitc3476b5ca7ab5237d3cb8a84fcb7a3292237b764 (patch)
treed0f276807882f005d964f62a2dc1f3691370b09d /nova/api
parentf348875724d2b46dedc7f090d4f8cf78b194a15a (diff)
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
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/ec2/cloud.py81
-rw-r--r--nova/api/openstack/compute/servers.py25
2 files changed, 32 insertions, 74 deletions
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'])