diff options
-rw-r--r-- | nova/tests/test_libvirt.py | 68 | ||||
-rw-r--r-- | nova/virt/driver.py | 4 | ||||
-rw-r--r-- | nova/virt/fake.py | 3 | ||||
-rw-r--r-- | nova/virt/hyperv/driver.py | 5 | ||||
-rw-r--r-- | nova/virt/hyperv/vmops.py | 26 | ||||
-rw-r--r-- | nova/virt/libvirt/driver.py | 37 | ||||
-rw-r--r-- | nova/virt/powervm/driver.py | 5 | ||||
-rw-r--r-- | nova/virt/powervm/operator.py | 8 | ||||
-rw-r--r-- | nova/virt/vmwareapi/driver.py | 5 | ||||
-rw-r--r-- | nova/virt/vmwareapi/vmops.py | 52 | ||||
-rw-r--r-- | nova/virt/xenapi/driver.py | 6 | ||||
-rw-r--r-- | nova/virt/xenapi/vmops.py | 17 |
12 files changed, 161 insertions, 75 deletions
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index a64b72695..a3529cecd 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -2760,6 +2760,74 @@ class LibvirtConnTestCase(test.TestCase): instance = db.instance_create(self.context, self.test_instance) conn.destroy(instance, {}) + def test_destroy_removes_disk(self): + instance = {"name": "instancename", "id": "instanceid", + "uuid": "875a8070-d0b9-4949-8b31-104d125c9a64"} + + self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, + '_undefine_domain') + libvirt_driver.LibvirtDriver._undefine_domain(instance) + self.mox.StubOutWithMock(shutil, "rmtree") + shutil.rmtree(os.path.join(CONF.instances_path, instance['name'])) + self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_cleanup_lvm') + libvirt_driver.LibvirtDriver._cleanup_lvm(instance) + + # Start test + self.mox.ReplayAll() + + def fake_destroy(instance): + pass + + def fake_os_path_exists(path): + return True + + def fake_unplug_vifs(instance, network_info): + pass + + def fake_unfilter_instance(instance, network_info): + pass + + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + + self.stubs.Set(conn, '_destroy', fake_destroy) + self.stubs.Set(conn, 'unplug_vifs', fake_unplug_vifs) + self.stubs.Set(conn.firewall_driver, + 'unfilter_instance', fake_unfilter_instance) + self.stubs.Set(os.path, 'exists', fake_os_path_exists) + conn.destroy(instance, []) + + def test_destroy_not_removes_disk(self): + instance = {"name": "instancename", "id": "instanceid", + "uuid": "875a8070-d0b9-4949-8b31-104d125c9a64"} + + self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, + '_undefine_domain') + libvirt_driver.LibvirtDriver._undefine_domain(instance) + + # Start test + self.mox.ReplayAll() + + def fake_destroy(instance): + pass + + def fake_os_path_exists(path): + return True + + def fake_unplug_vifs(instance, network_info): + pass + + def fake_unfilter_instance(instance, network_info): + pass + + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + + self.stubs.Set(conn, '_destroy', fake_destroy) + self.stubs.Set(conn, 'unplug_vifs', fake_unplug_vifs) + self.stubs.Set(conn.firewall_driver, + 'unfilter_instance', fake_unfilter_instance) + self.stubs.Set(os.path, 'exists', fake_os_path_exists) + conn.destroy(instance, [], None, False) + def test_destroy_undefines(self): mock = self.mox.CreateMock(libvirt.virDomain) mock.destroy() diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 7d627e80c..6718153e1 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -194,7 +194,8 @@ class ComputeDriver(object): """ raise NotImplementedError() - def destroy(self, instance, network_info, block_device_info=None): + def destroy(self, instance, network_info, block_device_info=None, + destroy_disks=True): """Destroy (shutdown and delete) the specified instance. If the instance is not found (for example if networking failed), this @@ -206,6 +207,7 @@ class ComputeDriver(object): :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` :param block_device_info: Information about block devices that should be detached from the instance. + :param destroy_disks: Indicates if disks should be destroyed """ # TODO(Vek): Need to pass context in for access to auth_token diff --git a/nova/virt/fake.py b/nova/virt/fake.py index c044a9abf..5ae172e1f 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -189,7 +189,8 @@ class FakeDriver(driver.ComputeDriver): def resume(self, instance, network_info, block_device_info=None): pass - def destroy(self, instance, network_info, block_device_info=None): + def destroy(self, instance, network_info, block_device_info=None, + destroy_disks=True): key = instance['name'] if key in self.instances: del self.instances[key] diff --git a/nova/virt/hyperv/driver.py b/nova/virt/hyperv/driver.py index 2b57ba0b1..62cb46f2f 100644 --- a/nova/virt/hyperv/driver.py +++ b/nova/virt/hyperv/driver.py @@ -98,8 +98,9 @@ class HyperVDriver(driver.ComputeDriver): block_device_info=None): self._vmops.reboot(instance, network_info, reboot_type) - def destroy(self, instance, network_info=None, cleanup=True): - self._vmops.destroy(instance, network_info, cleanup) + def destroy(self, instance, network_info=None, cleanup=True, + destroy_disks=True): + self._vmops.destroy(instance, network_info, cleanup, destroy_disks) def get_info(self, instance): return self._vmops.get_info(instance) diff --git a/nova/virt/hyperv/vmops.py b/nova/virt/hyperv/vmops.py index 680d57ce1..2c3253685 100644 --- a/nova/virt/hyperv/vmops.py +++ b/nova/virt/hyperv/vmops.py @@ -448,7 +448,8 @@ class VMOps(baseops.BaseOps): raise exception.InstanceNotFound(instance_id=instance["id"]) self._set_vm_state(instance['name'], 'Reboot') - def destroy(self, instance, network_info=None, cleanup=True): + def destroy(self, instance, network_info=None, cleanup=True, + destroy_disks=True): """Destroy the VM. Also destroy the associated VHD disk files""" LOG.debug(_("Got request to destroy vm %s"), instance['name']) vm = self._vmutils.lookup(self._conn, instance['name']) @@ -486,17 +487,18 @@ class VMOps(baseops.BaseOps): if not success: raise vmutils.HyperVException(_('Failed to destroy vm %s') % instance['name']) - #Disconnect volumes - for volume_drive in volumes_drives_list: - self._volumeops.disconnect_volume(volume_drive) - #Delete associated vhd disk files. - for disk in disk_files: - vhdfile = self._conn_cimv2.query( - "Select * from CIM_DataFile where Name = '" + - disk.replace("'", "''") + "'")[0] - LOG.debug(_("Del: disk %(vhdfile)s vm %(name)s") - % {'vhdfile': vhdfile, 'name': instance['name']}) - vhdfile.Delete() + if destroy_disks: + #Disconnect volumes + for volume_drive in volumes_drives_list: + self._volumeops.disconnect_volume(volume_drive) + #Delete associated vhd disk files. + for disk in disk_files: + vhdfile = self._conn_cimv2.query( + "Select * from CIM_DataFile where Name = '" + + disk.replace("'", "''") + "'")[0] + LOG.debug(_("Del: disk %(vhdfile)s vm %(name)s") + % {'vhdfile': vhdfile, 'name': instance['name']}) + vhdfile.Delete() def pause(self, instance): """Pause VM instance.""" diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index f360e44bc..202da220b 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -516,9 +516,10 @@ class LibvirtDriver(driver.ComputeDriver): timer = utils.FixedIntervalLoopingCall(_wait_for_destroy) timer.start(interval=0.5).wait() - def destroy(self, instance, network_info, block_device_info=None): + def destroy(self, instance, network_info, block_device_info=None, + destroy_disks=True): self._destroy(instance) - self._cleanup(instance, network_info, block_device_info) + self._cleanup(instance, network_info, block_device_info, destroy_disks) def _undefine_domain(self, instance): try: @@ -551,7 +552,8 @@ class LibvirtDriver(driver.ComputeDriver): locals(), instance=instance) raise - def _cleanup(self, instance, network_info, block_device_info): + def _cleanup(self, instance, network_info, block_device_info, + destroy_disks): self._undefine_domain(instance) self.unplug_vifs(instance, network_info) try: @@ -575,21 +577,22 @@ class LibvirtDriver(driver.ComputeDriver): connection_info, mount_device) - target = os.path.join(CONF.instances_path, instance['name']) - LOG.info(_('Deleting instance files %(target)s') % locals(), - instance=instance) - if os.path.exists(target): - # If we fail to get rid of the directory - # tree, this shouldn't block deletion of - # the instance as whole. - try: - shutil.rmtree(target) - except OSError, e: - LOG.error(_("Failed to cleanup directory %(target)s: %(e)s") % - locals()) + if destroy_disks: + target = os.path.join(CONF.instances_path, instance['name']) + LOG.info(_('Deleting instance files %(target)s') % locals(), + instance=instance) + if os.path.exists(target): + # If we fail to get rid of the directory + # tree, this shouldn't block deletion of + # the instance as whole. + try: + shutil.rmtree(target) + except OSError, e: + LOG.error(_("Failed to cleanup directory %(target)s: %(e)s" + ) % locals()) - #NOTE(bfilippov): destroy all LVM disks for this instance - self._cleanup_lvm(instance) + #NOTE(bfilippov): destroy all LVM disks for this instance + self._cleanup_lvm(instance) def _cleanup_lvm(self, instance): """Delete all LVM disks for given instance object""" diff --git a/nova/virt/powervm/driver.py b/nova/virt/powervm/driver.py index b9acc685c..5696bad87 100644 --- a/nova/virt/powervm/driver.py +++ b/nova/virt/powervm/driver.py @@ -100,9 +100,10 @@ class PowerVMDriver(driver.ComputeDriver): """Create a new instance/VM/domain on powerVM.""" self._powervm.spawn(context, instance, image_meta['id']) - def destroy(self, instance, network_info, block_device_info=None): + def destroy(self, instance, network_info, block_device_info=None, + destroy_disks=True): """Destroy (shutdown and delete) the specified instance.""" - self._powervm.destroy(instance['name']) + self._powervm.destroy(instance['name'], destroy_disks) def reboot(self, instance, network_info, reboot_type, block_device_info=None): diff --git a/nova/virt/powervm/operator.py b/nova/virt/powervm/operator.py index 46ead80d5..f659f1ba7 100644 --- a/nova/virt/powervm/operator.py +++ b/nova/virt/powervm/operator.py @@ -276,13 +276,13 @@ class PowerVMOperator(object): LOG.info(_("Instance spawned in %s seconds") % spawn_time, instance=instance) - def destroy(self, instance_name): + def destroy(self, instance_name, destroy_disks=True): """Destroy (shutdown and delete) the specified instance. :param instance_name: Instance name. """ try: - self._cleanup(instance_name) + self._cleanup(instance_name, destroy_disks) except exception.PowerVMLPARInstanceNotFound: LOG.warn(_("During destroy, LPAR instance '%s' was not found on " "PowerVM system.") % instance_name) @@ -317,7 +317,7 @@ class PowerVMOperator(object): if previous_state == 'Running': self.power_on(instance['name']) - def _cleanup(self, instance_name): + def _cleanup(self, instance_name, destroy_disks=True): lpar_id = self._get_instance(instance_name)['lpar_id'] try: vhost = self._operator.get_vhost_by_instance_id(lpar_id) @@ -326,7 +326,7 @@ class PowerVMOperator(object): LOG.debug(_("Shutting down the instance '%s'") % instance_name) self._operator.stop_lpar(instance_name) - if disk_name: + if disk_name and destroy_disks: # TODO(mrodden): we should also detach from the instance # before we start deleting things... self._disk_adapter.detach_volume_from_host(disk_name) diff --git a/nova/virt/vmwareapi/driver.py b/nova/virt/vmwareapi/driver.py index 50fc3e922..ce8005861 100644 --- a/nova/virt/vmwareapi/driver.py +++ b/nova/virt/vmwareapi/driver.py @@ -136,9 +136,10 @@ class VMWareESXDriver(driver.ComputeDriver): """Reboot VM instance.""" self._vmops.reboot(instance, network_info) - def destroy(self, instance, network_info, block_device_info=None): + def destroy(self, instance, network_info, block_device_info=None, + destroy_disks=True): """Destroy VM instance.""" - self._vmops.destroy(instance, network_info) + self._vmops.destroy(instance, network_info, destroy_disks) def pause(self, instance): """Pause VM instance.""" diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index b5b5d1fff..e591245e2 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -539,7 +539,7 @@ class VMWareVMOps(object): self._session._wait_for_task(instance['uuid'], reset_task) LOG.debug(_("Did hard reboot of VM"), instance=instance) - def destroy(self, instance, network_info): + def destroy(self, instance, network_info, destroy_disks=True): """ Destroy a VM instance. Steps followed are: 1. Power off the VM, if it is in poweredOn state. @@ -590,30 +590,32 @@ class VMWareVMOps(object): # Delete the folder holding the VM related content on # the datastore. - try: - dir_ds_compliant_path = vm_util.build_datastore_path( - datastore_name, - os.path.dirname(vmx_file_path)) - LOG.debug(_("Deleting contents of the VM from " - "datastore %(datastore_name)s") % - {'datastore_name': datastore_name}, - instance=instance) - delete_task = self._session._call_method( - self._session._get_vim(), - "DeleteDatastoreFile_Task", - self._session._get_vim().get_service_content().fileManager, - name=dir_ds_compliant_path) - self._session._wait_for_task(instance['uuid'], delete_task) - LOG.debug(_("Deleted contents of the VM from " - "datastore %(datastore_name)s") % - {'datastore_name': datastore_name}, - instance=instance) - except Exception, excep: - LOG.warn(_("In vmwareapi:vmops:destroy, " - "got this exception while deleting" - " the VM contents from the disk: %s") - % str(excep), - instance=instance) + if destroy_disks: + try: + dir_ds_compliant_path = vm_util.build_datastore_path( + datastore_name, + os.path.dirname(vmx_file_path)) + LOG.debug(_("Deleting contents of the VM from " + "datastore %(datastore_name)s") % + {'datastore_name': datastore_name}, + instance=instance) + vim = self._session._get_vim() + delete_task = self._session._call_method( + vim, + "DeleteDatastoreFile_Task", + vim.get_service_content().fileManager, + name=dir_ds_compliant_path) + self._session._wait_for_task(instance['uuid'], delete_task) + LOG.debug(_("Deleted contents of the VM from " + "datastore %(datastore_name)s") % + {'datastore_name': datastore_name}, + instance=instance) + except Exception, excep: + LOG.warn(_("In vmwareapi:vmops:destroy, " + "got this exception while deleting" + " the VM contents from the disk: %s") + % str(excep), + instance=instance) except Exception, exc: LOG.exception(exc, instance=instance) diff --git a/nova/virt/xenapi/driver.py b/nova/virt/xenapi/driver.py index a03e5eab1..1b60faa9f 100644 --- a/nova/virt/xenapi/driver.py +++ b/nova/virt/xenapi/driver.py @@ -211,9 +211,11 @@ class XenAPIDriver(driver.ComputeDriver): """Apply a diff to the instance metadata.""" self._vmops.change_instance_metadata(instance, diff) - def destroy(self, instance, network_info, block_device_info=None): + def destroy(self, instance, network_info, block_device_info=None, + destroy_disks=True): """Destroy VM instance""" - self._vmops.destroy(instance, network_info, block_device_info) + self._vmops.destroy(instance, network_info, block_device_info, + destroy_disks) def pause(self, instance): """Pause VM instance""" diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 06e950f43..d3dfdd539 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -1067,7 +1067,8 @@ class VMOps(object): # Destroy Rescue VM self._session.call_xenapi("VM.destroy", rescue_vm_ref) - def destroy(self, instance, network_info, block_device_info=None): + def destroy(self, instance, network_info, block_device_info=None, + destroy_disks=True): """Destroy VM instance. This is the method exposed by xenapi_conn.destroy(). The rest of the @@ -1087,10 +1088,11 @@ class VMOps(object): self._destroy_rescue_instance(rescue_vm_ref, vm_ref) return self._destroy(instance, vm_ref, network_info, - block_device_info=block_device_info) + block_device_info=block_device_info, + destroy_disks=destroy_disks) def _destroy(self, instance, vm_ref, network_info=None, - block_device_info=None): + block_device_info=None, destroy_disks=True): """Destroys VM instance by performing: 1. A shutdown @@ -1106,10 +1108,11 @@ class VMOps(object): vm_utils.hard_shutdown_vm(self._session, instance, vm_ref) - # Destroy VDIs - self._detach_vm_vols(instance, vm_ref, block_device_info) - self._destroy_vdis(instance, vm_ref, block_device_info) - self._destroy_kernel_ramdisk(instance, vm_ref) + # Destroy VDIs (if necessary) + if destroy_disks: + self._detach_vm_vols(instance, vm_ref, block_device_info) + self._destroy_vdis(instance, vm_ref, block_device_info) + self._destroy_kernel_ramdisk(instance, vm_ref) vm_utils.destroy_vm(self._session, instance, vm_ref) |