From ec7815960386ba9f522b839144d57a423b930654 Mon Sep 17 00:00:00 2001 From: Gary Kotton Date: Mon, 6 Jul 2015 03:57:17 -0700 Subject: [PATCH] VMware: treat deletion exception with attached volumes Enable the OpenStack user to successfully delete an instance that was running on an ESX that has been removed, or the instance was deleted from the backend VC. In the event that the instance would have an attached volume then deleting the instance would not handle the exception ManagedObjectNotFound. Change-Id: I1781256bec15ed3301425e63c7a3d28dc057f83e Closes-bug: #1471751 --- .../unit/virt/vmwareapi/test_driver_api.py | 19 +++++++ nova/virt/vmwareapi/driver.py | 55 +++++++++++-------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/nova/tests/unit/virt/vmwareapi/test_driver_api.py b/nova/tests/unit/virt/vmwareapi/test_driver_api.py index 91fa3a9557..b19e194c5d 100644 --- a/nova/tests/unit/virt/vmwareapi/test_driver_api.py +++ b/nova/tests/unit/virt/vmwareapi/test_driver_api.py @@ -680,6 +680,25 @@ class VMwareAPIVMTestCase(test.NoDBTestCase): connection_info, self.instance, 'fake-name') mock_destroy.assert_called_once_with(self.instance, True) + @mock.patch.object(vmops.VMwareVMOps, 'power_off', + side_effect=vexc.ManagedObjectNotFoundException()) + @mock.patch.object(vmops.VMwareVMOps, 'destroy') + def test_destroy_with_attached_volumes_missing(self, + mock_destroy, + mock_power_off): + self._create_vm() + connection_info = {'data': 'fake-data', 'serial': 'volume-fake-id'} + bdm = [{'connection_info': connection_info, + 'disk_bus': 'fake-bus', + 'device_name': 'fake-name', + 'mount_device': '/dev/sdb'}] + bdi = {'block_device_mapping': bdm, 'root_device_name': '/dev/sda'} + self.assertNotEqual(vm_states.STOPPED, self.instance.vm_state) + self.conn.destroy(self.context, self.instance, self.network_info, + block_device_info=bdi) + mock_power_off.assert_called_once_with(self.instance) + mock_destroy.assert_called_once_with(self.instance, True) + @mock.patch.object(driver.VMwareVCDriver, 'detach_volume', side_effect=exception.StorageError(reason='oh man')) @mock.patch.object(vmops.VMwareVMOps, 'destroy') diff --git a/nova/virt/vmwareapi/driver.py b/nova/virt/vmwareapi/driver.py index fd7616e726..cb99efe1c3 100644 --- a/nova/virt/vmwareapi/driver.py +++ b/nova/virt/vmwareapi/driver.py @@ -27,6 +27,7 @@ from oslo_log import versionutils from oslo_serialization import jsonutils from oslo_utils import excutils from oslo_vmware import api +from oslo_vmware import exceptions as vexc from oslo_vmware import pbm from oslo_vmware import vim from oslo_vmware import vim_util @@ -573,6 +574,31 @@ class VMwareVCDriver(driver.ComputeDriver): """Reboot VM instance.""" self._vmops.reboot(instance, network_info, reboot_type) + def _detach_instance_volumes(self, instance, block_device_info): + # We need to detach attached volumes + block_device_mapping = driver.block_device_info_get_mapping( + block_device_info) + if block_device_mapping: + # Certain disk types, for example 'IDE' do not support hot + # plugging. Hence we need to power off the instance and update + # the instance state. + self._vmops.power_off(instance) + # TODO(garyk): update the volumeops to read the state form the + # VM instead of relying on a instance flag + instance.vm_state = vm_states.STOPPED + for disk in block_device_mapping: + connection_info = disk['connection_info'] + try: + self.detach_volume(connection_info, instance, + disk.get('device_name')) + except Exception as e: + with excutils.save_and_reraise_exception(): + LOG.error(_LE("Failed to detach %(device_name)s. " + "Exception: %(exc)s"), + {'device_name': disk.get('device_name'), + 'exc': e}, + instance=instance) + def destroy(self, context, instance, network_info, block_device_info=None, destroy_disks=True, migrate_data=None): """Destroy VM instance.""" @@ -590,29 +616,12 @@ class VMwareVCDriver(driver.ComputeDriver): # We need to detach attached volumes if block_device_info is not None: - block_device_mapping = driver.block_device_info_get_mapping( - block_device_info) - if block_device_mapping: - # Certain disk types, for example 'IDE' do not support hot - # plugging. Hence we need to power off the instance and update - # the instance state. - self._vmops.power_off(instance) - # TODO(garyk): update the volumeops to read the state form the - # VM instead of relying on a instance flag - instance.vm_state = vm_states.STOPPED - for disk in block_device_mapping: - connection_info = disk['connection_info'] - try: - self.detach_volume(connection_info, instance, - disk.get('device_name')) - except Exception as e: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Failed to detach %(device_name)s. " - "Exception: %(exc)s"), - {'device_name': disk.get('device_name'), - 'exc': e}, - instance=instance) - + try: + self._detach_instance_volumes(instance, block_device_info) + except vexc.ManagedObjectNotFoundException: + LOG.warning(_LW('Instance does not exists. Proceeding to ' + 'delete instance properties on datastore'), + instance=instance) self._vmops.destroy(instance, destroy_disks) def pause(self, instance):