summaryrefslogtreecommitdiff
path: root/nova/virt/libvirt/driver.py
diff options
context:
space:
mode:
Diffstat (limited to 'nova/virt/libvirt/driver.py')
-rw-r--r--nova/virt/libvirt/driver.py181
1 files changed, 137 insertions, 44 deletions
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index 469de4afdb..4432fc8d05 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -1048,6 +1048,71 @@ class LibvirtDriver(driver.ComputeDriver):
def cleanup(self, context, instance, network_info, block_device_info=None,
destroy_disks=True, migrate_data=None, destroy_vifs=True):
+ """Cleanup the instance from the host.
+
+ Identify if the instance disks and instance path should be removed
+ from the host before calling down into the _cleanup method for the
+ actual removal of resources from the host.
+
+ :param context: security context
+ :param instance: instance object for the instance being cleaned up
+ :param network_info: instance network information
+ :param block_device_info: optional instance block device information
+ :param destroy_disks: if local ephemeral disks should be destroyed
+ :param migrate_data: optional migrate_data object
+ :param destroy_vifs: if plugged vifs should be unplugged
+ """
+ cleanup_instance_dir = False
+ cleanup_instance_disks = False
+ # We assume destroy_disks means destroy instance directory and disks
+ if destroy_disks:
+ cleanup_instance_dir = True
+ cleanup_instance_disks = True
+ else:
+ # NOTE(mdbooth): I think the theory here was that if this is a
+ # migration with shared block storage then we need to delete the
+ # instance directory because that's not shared. I'm pretty sure
+ # this is wrong.
+ if migrate_data and 'is_shared_block_storage' in migrate_data:
+ cleanup_instance_dir = migrate_data.is_shared_block_storage
+
+ # NOTE(lyarwood): The following workaround allows operators to
+ # ensure that non-shared instance directories are removed after an
+ # evacuation or revert resize when using the shared RBD
+ # imagebackend. This workaround is not required when cleaning up
+ # migrations that provide migrate_data to this method as the
+ # existing is_shared_block_storage conditional will cause the
+ # instance directory to be removed.
+ if not cleanup_instance_dir:
+ if CONF.workarounds.ensure_libvirt_rbd_instance_dir_cleanup:
+ cleanup_instance_dir = CONF.libvirt.images_type == 'rbd'
+
+ return self._cleanup(
+ context, instance, network_info,
+ block_device_info=block_device_info,
+ destroy_vifs=destroy_vifs,
+ cleanup_instance_dir=cleanup_instance_dir,
+ cleanup_instance_disks=cleanup_instance_disks)
+
+ def _cleanup(self, context, instance, network_info, block_device_info=None,
+ destroy_vifs=True, cleanup_instance_dir=False,
+ cleanup_instance_disks=False):
+ """Cleanup the domain and any attached resources from the host.
+
+ This method cleans up any pmem devices, unplugs VIFs, disconnects
+ attached volumes and undefines the instance domain within libvirt.
+ It also optionally removes the ephemeral disks and the instance
+ directory from the host depending on the cleanup_instance_dir|disks
+ kwargs provided.
+
+ :param context: security context
+ :param instance: instance object for the instance being cleaned up
+ :param network_info: instance network information
+ :param block_device_info: optional instance block device information
+ :param destroy_vifs: if plugged vifs should be unplugged
+ :param cleanup_instance_dir: If the instance dir should be removed
+ :param cleanup_instance_disks: If the instance disks should be removed
+ """
if destroy_vifs:
self._unplug_vifs(instance, network_info, True)
@@ -1097,7 +1162,7 @@ class LibvirtDriver(driver.ComputeDriver):
self._disconnect_volume(context, connection_info, instance)
except Exception as exc:
with excutils.save_and_reraise_exception() as ctxt:
- if destroy_disks:
+ if cleanup_instance_disks:
# Don't block on Volume errors if we're trying to
# delete the instance as we may be partially created
# or deleted
@@ -1109,26 +1174,14 @@ class LibvirtDriver(driver.ComputeDriver):
'exc': encodeutils.exception_to_unicode(exc)},
instance=instance)
- if destroy_disks:
+ if cleanup_instance_disks:
# NOTE(haomai): destroy volumes if needed
if CONF.libvirt.images_type == 'lvm':
self._cleanup_lvm(instance, block_device_info)
if CONF.libvirt.images_type == 'rbd':
self._cleanup_rbd(instance)
- is_shared_block_storage = False
- if migrate_data and 'is_shared_block_storage' in migrate_data:
- is_shared_block_storage = migrate_data.is_shared_block_storage
- # NOTE(lyarwood): The following workaround allows operators to ensure
- # that non-shared instance directories are removed after an evacuation
- # or revert resize when using the shared RBD imagebackend. This
- # workaround is not required when cleaning up migrations that provide
- # migrate_data to this method as the existing is_shared_block_storage
- # conditional will cause the instance directory to be removed.
- if ((destroy_disks or is_shared_block_storage) or
- (CONF.workarounds.ensure_libvirt_rbd_instance_dir_cleanup and
- CONF.libvirt.images_type == 'rbd')):
-
+ if cleanup_instance_dir:
attempts = int(instance.system_metadata.get('clean_attempts',
'0'))
success = self.delete_instance_files(instance)
@@ -3149,9 +3202,10 @@ class LibvirtDriver(driver.ComputeDriver):
gen_confdrive = functools.partial(self._create_configdrive,
context, instance,
injection_info)
- self._create_image(context, instance, disk_info['mapping'],
- injection_info=injection_info,
- block_device_info=block_device_info)
+ created_instance_dir, created_disks = self._create_image(
+ context, instance, disk_info['mapping'],
+ injection_info=injection_info,
+ block_device_info=block_device_info)
# Required by Quobyte CI
self._ensure_console_log_for_instance(instance)
@@ -3167,7 +3221,8 @@ class LibvirtDriver(driver.ComputeDriver):
context, xml, instance, network_info,
block_device_info=block_device_info,
post_xml_callback=gen_confdrive,
- destroy_disks_on_failure=True)
+ cleanup_instance_dir=created_instance_dir,
+ cleanup_instance_disks=created_disks)
LOG.debug("Guest created on hypervisor", instance=instance)
def _wait_for_boot():
@@ -3459,8 +3514,17 @@ class LibvirtDriver(driver.ComputeDriver):
def raw(fname):
return image(fname, image_type='raw')
+ created_instance_dir = True
+
# ensure directories exist and are writable
- fileutils.ensure_tree(libvirt_utils.get_instance_path(instance))
+ instance_dir = libvirt_utils.get_instance_path(instance)
+ if os.path.exists(instance_dir):
+ LOG.debug("Instance directory exists: not creating",
+ instance=instance)
+ created_instance_dir = False
+ else:
+ LOG.debug("Creating instance directory", instance=instance)
+ fileutils.ensure_tree(libvirt_utils.get_instance_path(instance))
LOG.info('Creating image', instance=instance)
@@ -3502,6 +3566,10 @@ class LibvirtDriver(driver.ComputeDriver):
'kernel_id': instance.kernel_id,
'ramdisk_id': instance.ramdisk_id}
+ # NOTE(mdbooth): kernel and ramdisk, if they are defined, are hardcoded
+ # to use raw, which means they will always be cleaned up with the
+ # instance directory. We must not consider them for created_disks,
+ # which may not be using the instance directory.
if disk_images['kernel_id']:
fname = imagecache.get_cache_fname(disk_images['kernel_id'])
raw('kernel').cache(fetch_func=libvirt_utils.fetch_raw_image,
@@ -3521,10 +3589,9 @@ class LibvirtDriver(driver.ComputeDriver):
uid = pwd.getpwnam('root').pw_uid
nova.privsep.path.chown(image('disk').path, uid=uid)
- self._create_and_inject_local_root(context, instance,
- booted_from_volume, suffix,
- disk_images, injection_info,
- fallback_from_host)
+ created_disks = self._create_and_inject_local_root(
+ context, instance, booted_from_volume, suffix, disk_images,
+ injection_info, fallback_from_host)
# Lookup the filesystem type if required
os_type_with_default = disk_api.get_fs_type_for_os_type(
@@ -3538,6 +3605,9 @@ class LibvirtDriver(driver.ComputeDriver):
ephemeral_gb = instance.flavor.ephemeral_gb
if 'disk.local' in disk_mapping:
disk_image = image('disk.local')
+ # Short circuit the exists() tests if we already created a disk
+ created_disks = created_disks or not disk_image.exists()
+
fn = functools.partial(self._create_ephemeral,
fs_label='ephemeral0',
os_type=instance.os_type,
@@ -3554,6 +3624,8 @@ class LibvirtDriver(driver.ComputeDriver):
for idx, eph in enumerate(driver.block_device_info_get_ephemerals(
block_device_info)):
disk_image = image(blockinfo.get_eph_disk(idx))
+ # Short circuit the exists() tests if we already created a disk
+ created_disks = created_disks or not disk_image.exists()
specified_fs = eph.get('guest_format')
if specified_fs and not self.is_supported_fs_format(specified_fs):
@@ -3576,15 +3648,25 @@ class LibvirtDriver(driver.ComputeDriver):
if swap_mb > 0:
size = swap_mb * units.Mi
- image('disk.swap').cache(fetch_func=self._create_swap,
- context=context,
- filename="swap_%s" % swap_mb,
- size=size,
- swap_mb=swap_mb)
+ swap = image('disk.swap')
+ # Short circuit the exists() tests if we already created a disk
+ created_disks = created_disks or not swap.exists()
+ swap.cache(fetch_func=self._create_swap, context=context,
+ filename="swap_%s" % swap_mb,
+ size=size, swap_mb=swap_mb)
+
+ if created_disks:
+ LOG.debug('Created local disks', instance=instance)
+ else:
+ LOG.debug('Did not create local disks', instance=instance)
+
+ return (created_instance_dir, created_disks)
def _create_and_inject_local_root(self, context, instance,
booted_from_volume, suffix, disk_images,
injection_info, fallback_from_host):
+ created_disks = False
+
# File injection only if needed
need_inject = (not configdrive.required_by(instance) and
injection_info is not None and
@@ -3602,6 +3684,8 @@ class LibvirtDriver(driver.ComputeDriver):
backend = self.image_backend.by_name(instance, 'disk' + suffix,
CONF.libvirt.images_type)
+ created_disks = not backend.exists()
+
if instance.task_state == task_states.RESIZE_FINISH:
backend.create_snap(libvirt_utils.RESIZE_SNAPSHOT_NAME)
if backend.SUPPORTS_CLONE:
@@ -3624,6 +3708,8 @@ class LibvirtDriver(driver.ComputeDriver):
LOG.warning('File injection into a boot from volume '
'instance is not supported', instance=instance)
+ return created_disks
+
def _create_configdrive(self, context, instance, injection_info,
rescue=False):
# As this method being called right after the definition of a
@@ -5595,20 +5681,25 @@ class LibvirtDriver(driver.ComputeDriver):
for vif in network_info if vif.get('active', True) is False]
def _cleanup_failed_start(self, context, instance, network_info,
- block_device_info, guest, destroy_disks):
+ block_device_info, guest,
+ cleanup_instance_dir=False,
+ cleanup_instance_disks=False):
try:
if guest and guest.is_active():
guest.poweroff()
finally:
- self.cleanup(context, instance, network_info=network_info,
- block_device_info=block_device_info,
- destroy_disks=destroy_disks)
+ self._cleanup(context, instance, network_info,
+ block_device_info=block_device_info,
+ destroy_vifs=True,
+ cleanup_instance_dir=cleanup_instance_dir,
+ cleanup_instance_disks=cleanup_instance_disks)
def _create_domain_and_network(self, context, xml, instance, network_info,
block_device_info=None, power_on=True,
vifs_already_plugged=False,
post_xml_callback=None,
- destroy_disks_on_failure=False):
+ cleanup_instance_dir=False,
+ cleanup_instance_disks=False):
"""Do required network setup and create domain."""
timeout = CONF.vif_plugging_timeout
@@ -5643,9 +5734,10 @@ class LibvirtDriver(driver.ComputeDriver):
# Neutron reported failure and we didn't swallow it, so
# bail here
with excutils.save_and_reraise_exception():
- self._cleanup_failed_start(context, instance, network_info,
- block_device_info, guest,
- destroy_disks_on_failure)
+ self._cleanup_failed_start(
+ context, instance, network_info, block_device_info, guest,
+ cleanup_instance_dir=cleanup_instance_dir,
+ cleanup_instance_disks=cleanup_instance_disks)
except eventlet.timeout.Timeout:
# We never heard from Neutron
LOG.warning('Timeout waiting for %(events)s for '
@@ -5656,18 +5748,19 @@ class LibvirtDriver(driver.ComputeDriver):
'task_state': instance.task_state},
instance=instance)
if CONF.vif_plugging_is_fatal:
- self._cleanup_failed_start(context, instance, network_info,
- block_device_info, guest,
- destroy_disks_on_failure)
+ self._cleanup_failed_start(
+ context, instance, network_info, block_device_info, guest,
+ cleanup_instance_dir=cleanup_instance_dir,
+ cleanup_instance_disks=cleanup_instance_disks)
raise exception.VirtualInterfaceCreateException()
except Exception:
# Any other error, be sure to clean up
LOG.error('Failed to start libvirt guest', instance=instance)
with excutils.save_and_reraise_exception():
- self._cleanup_failed_start(context, instance, network_info,
- block_device_info, guest,
- destroy_disks_on_failure)
-
+ self._cleanup_failed_start(
+ context, instance, network_info, block_device_info, guest,
+ cleanup_instance_dir=cleanup_instance_dir,
+ cleanup_instance_disks=cleanup_instance_disks)
# Resume only if domain has been paused
if pause:
guest.resume()