diff options
| author | Renuka Apte <renuka.apte@citrix.com> | 2012-09-04 23:02:16 -0700 |
|---|---|---|
| committer | Clay Gerrard <clay.gerrard@gmail.com> | 2012-10-03 20:32:22 -0500 |
| commit | 359527501604ea720b594a2ed1b32cd29fe6adbc (patch) | |
| tree | 2e938a604d06016107f0782d7971a18beb33e936 /nova/virt | |
| parent | 1d4506c16aec9674be6a3685ba585a8bbd9c1559 (diff) | |
xenapi: Refactor snapshots during resize
Currently, we use VM.snapshot for resize, which fails if we have a
volume attached to the VM, which does not support snapshots. This change
uses VDI.snapshot instead, for all VDIs that are not attached by nova.
Also needed for xenapi: detaching and reattaching volumes during
migrations and reverting of migrations.
Fixes Bug #1028092
Change-Id: I3e2973747135a9c33de194e38537620c397bb87e
Diffstat (limited to 'nova/virt')
| -rw-r--r-- | nova/virt/xenapi/driver.py | 27 | ||||
| -rw-r--r-- | nova/virt/xenapi/vm_utils.py | 48 | ||||
| -rw-r--r-- | nova/virt/xenapi/vmops.py | 20 | ||||
| -rw-r--r-- | nova/virt/xenapi/volumeops.py | 4 |
4 files changed, 76 insertions, 23 deletions
diff --git a/nova/virt/xenapi/driver.py b/nova/virt/xenapi/driver.py index 7d6ec37bb..04b75598c 100644 --- a/nova/virt/xenapi/driver.py +++ b/nova/virt/xenapi/driver.py @@ -188,6 +188,13 @@ class XenAPIDriver(driver.ComputeDriver): """Finish reverting a resize, powering back on the instance""" # NOTE(vish): Xen currently does not use network info. self._vmops.finish_revert_migration(instance) + block_device_mapping = driver.block_device_info_get_mapping( + block_device_info) + for vol in block_device_mapping: + connection_info = vol['connection_info'] + mount_device = vol['mount_device'].rpartition("/")[2] + self._volumeops.attach_volume(connection_info, + instance['name'], mount_device) def finish_migration(self, context, migration, instance, disk_info, network_info, image_meta, resize_instance=False, @@ -195,6 +202,13 @@ class XenAPIDriver(driver.ComputeDriver): """Completes a resize, turning on the migrated instance""" self._vmops.finish_migration(context, migration, instance, disk_info, network_info, image_meta, resize_instance) + block_device_mapping = driver.block_device_info_get_mapping( + block_device_info) + for vol in block_device_mapping: + connection_info = vol['connection_info'] + mount_device = vol['mount_device'].rpartition("/")[2] + self._volumeops.attach_volume(connection_info, + instance['name'], mount_device) def snapshot(self, context, instance, image_id): """ Create snapshot from a running VM instance """ @@ -237,8 +251,17 @@ class XenAPIDriver(driver.ComputeDriver): """Transfers the VHD of a running instance to another host, then shuts off the instance copies over the COW disk""" # NOTE(vish): Xen currently does not use network info. - return self._vmops.migrate_disk_and_power_off(context, instance, - dest, instance_type) + rv = self._vmops.migrate_disk_and_power_off(context, instance, + dest, instance_type) + block_device_mapping = driver.block_device_info_get_mapping( + block_device_info) + name_label = self._vmops._get_orig_vm_name_label(instance) + for vol in block_device_mapping: + connection_info = vol['connection_info'] + mount_device = vol['mount_device'].rpartition("/")[2] + self._volumeops.detach_volume(connection_info, + name_label, mount_device) + return rv def suspend(self, instance): """suspend the specified instance""" diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 7a37bcdac..764a0c59f 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -524,39 +524,51 @@ def snapshot_attached_here(session, instance, vm_ref, label): vm_vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) original_parent_uuid = _get_vhd_parent_uuid(session, vm_vdi_ref) - template_vm_ref, template_vdi_uuid = _create_snapshot( - session, instance, vm_ref, label) - try: + vdi_snapshot_recs = _vdi_snapshot_vm_base(session, instance, vm_ref) sr_ref = vm_vdi_rec["SR"] parent_uuid, base_uuid = _wait_for_vhd_coalesce( session, instance, sr_ref, vm_vdi_ref, original_parent_uuid) - vdi_uuids = [vdi_rec['uuid'] for vdi_rec in - _walk_vdi_chain(session, template_vdi_uuid)] + vdi_uuids = [] + for snapshot in vdi_snapshot_recs: + vdi_uuids += [vdi_rec['uuid'] for vdi_rec in + _walk_vdi_chain(session, snapshot['uuid'])] yield vdi_uuids finally: - _destroy_snapshot(session, instance, template_vm_ref) - + _destroy_snapshots(session, instance, vdi_snapshot_recs) -def _create_snapshot(session, instance, vm_ref, label): - template_vm_ref = session.call_xenapi('VM.snapshot', vm_ref, label) - template_vdi_rec = get_vdi_for_vm_safely(session, template_vm_ref)[1] - template_vdi_uuid = template_vdi_rec["uuid"] - LOG.debug(_("Created snapshot %(template_vdi_uuid)s with label" - " '%(label)s'"), locals(), instance=instance) +def _vdi_snapshot_vm_base(session, instance, vm_ref): + """Make a snapshot of every non-cinder VDI and return a list + of the new vdi records. + """ + new_vdis = [] + try: + vbd_refs = session.call_xenapi("VM.get_VBDs", vm_ref) + for vbd_ref in vbd_refs: + oc = session.call_xenapi("VBD.get_other_config", vbd_ref) + if 'osvol' not in oc: + # This volume is not a nova/cinder volume + vdi_ref = session.call_xenapi("VBD.get_VDI", vbd_ref) + snapshot_ref = session.call_xenapi("VDI.snapshot", vdi_ref, + {}) + new_vdis.append(session.call_xenapi("VDI.get_record", + snapshot_ref)) - return template_vm_ref, template_vdi_uuid + except session.XenAPI.Failure: + LOG.exception(_("Failed to snapshot VDI"), instance=instance) + raise + finally: + return new_vdis -def _destroy_snapshot(session, instance, vm_ref): - vdi_refs = lookup_vm_vdis(session, vm_ref) +def _destroy_snapshots(session, instance, vdi_snapshot_recs): + vdi_refs = [session.call_xenapi("VDI.get_by_uuid", vdi_rec['uuid']) + for vdi_rec in vdi_snapshot_recs] safe_destroy_vdis(session, vdi_refs) - destroy_vm(session, instance, vm_ref) - def get_sr_path(session): """Return the path to our storage repository diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ae8efc5f3..feee83b5d 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -192,10 +192,26 @@ class VMOps(object): if resize_instance: self._resize_instance(instance, root_vdi) + # Check if kernel and ramdisk are external + kernel_file = None + ramdisk_file = None + + name_label = instance['name'] + if instance['kernel_id']: + vdis = vm_utils.create_kernel_image(context, self._session, + instance, name_label, instance['kernel_id'], + vm_utils.ImageType.KERNEL) + kernel_file = vdis['kernel'].get('file') + if instance['ramdisk_id']: + vdis = vm_utils.create_kernel_image(context, self._session, + instance, name_label, instance['ramdisk_id'], + vm_utils.ImageType.RAMDISK) + ramdisk_file = vdis['ramdisk'].get('file') + vm_ref = self._create_vm(context, instance, instance['name'], {'root': root_vdi}, - network_info, image_meta) - + network_info, image_meta, kernel_file, + ramdisk_file) # 5. Start VM self._start(instance, vm_ref=vm_ref) self._update_instance_progress(context, instance, diff --git a/nova/virt/xenapi/volumeops.py b/nova/virt/xenapi/volumeops.py index ec495ebc1..09d735902 100644 --- a/nova/virt/xenapi/volumeops.py +++ b/nova/virt/xenapi/volumeops.py @@ -215,8 +215,10 @@ class VolumeOps(object): raise Exception(_('Unable to locate volume %s') % mountpoint) try: + vm_rec = self._session.call_xenapi("VM.get_record", vm_ref) sr_ref = volume_utils.find_sr_from_vbd(self._session, vbd_ref) - vm_utils.unplug_vbd(self._session, vbd_ref) + if vm_rec['power_state'] != 'Halted': + vm_utils.unplug_vbd(self._session, vbd_ref) except volume_utils.StorageError, exc: LOG.exception(exc) raise Exception(_('Unable to detach volume %s') % mountpoint) |
