VMware: bug fix for VM rescue when config drive is configured

The addition of the config drive feature broke the VM rescue. The
cause of the problem was setting the instance UUID to the name of
the rescue instance. This must be a UUID! The solution
is to pass the instance name to the spawn method instead of writing
this on the instance UUID.

Change-Id: Ia8494b0c8099753b666bda18a19c4c9a3f764616
Closes-bug: #1247427
This commit is contained in:
Gary Kotton 2013-11-02 10:13:54 -07:00
parent 7646c28315
commit 88861df2b9
4 changed files with 95 additions and 20 deletions

View File

@ -698,10 +698,19 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
self.assertIsNone(self.conn.destroy(self.context, self.instance,
self.network_info))
def _rescue(self):
def _rescue(self, config_drive=False):
def fake_attach_disk_to_vm(*args, **kwargs):
pass
if config_drive:
def fake_create_config_drive(instance, injected_files, password,
data_store_name, folder,
instance_uuid, cookies):
self.assertTrue(uuidutils.is_uuid_like(instance['uuid']))
self.stubs.Set(self.conn._vmops, '_create_config_drive',
fake_create_config_drive)
self._create_vm()
info = self.conn.get_info({'name': 1, 'uuid': self.uuid,
'node': self.instance_node})
@ -709,7 +718,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
fake_attach_disk_to_vm)
self.conn.rescue(self.context, self.instance, self.network_info,
self.image, 'fake-password')
info = self.conn.get_info({'name-rescue': 1,
info = self.conn.get_info({'name': '1-rescue',
'uuid': '%s-rescue' % self.uuid,
'node': self.instance_node})
self._check_vm_info(info, power_state.RUNNING)
@ -720,8 +729,19 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
def test_rescue(self):
self._rescue()
def test_rescue_with_config_drive(self):
self.flags(force_config_drive=True)
self._rescue(config_drive=True)
def test_unrescue(self):
self._rescue()
def fake_detach_disk_from_vm(*args, **kwargs):
pass
self.stubs.Set(self.conn._volumeops, "detach_disk_from_vm",
fake_detach_disk_from_vm)
self.conn.unrescue(self.instance, None)
info = self.conn.get_info({'name': 1, 'uuid': self.uuid,
'node': self.instance_node})

View File

@ -21,6 +21,7 @@ import re
from nova import exception
from nova.openstack.common.gettextutils import _
from nova.openstack.common import uuidutils
from nova import test
from nova import unit
from nova.virt.vmwareapi import fake
@ -397,3 +398,32 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
def test_detach_virtual_disk_destroy_spec(self):
self._test_detach_virtual_disk_spec(destroy_disk=True)
def test_get_vm_create_spec(self):
instance_uuid = uuidutils.generate_uuid()
fake_instance = {'id': 7, 'name': 'fake!',
'uuid': instance_uuid,
'vcpus': 2, 'memory_mb': 2048}
result = vm_util.get_vm_create_spec(fake.FakeFactory(),
fake_instance, 'fake-name',
'fake-datastore', [])
expected = """{
'files': {'vmPathName': '[fake-datastore]',
'obj_name': 'ns0:VirtualMachineFileInfo'},
'name': 'fake-name', 'deviceChange': [],
'extraConfig': [{'value': '%s',
'key': 'nvp.vm-uuid',
'obj_name': 'ns0:OptionValue'}],
'memoryMB': 2048,
'obj_name': 'ns0:VirtualMachineConfigSpec',
'guestId': 'otherGuest',
'tools': {'beforeGuestStandby': True,
'beforeGuestReboot': True,
'beforeGuestShutdown': True,
'afterResume': True,
'afterPowerOn': True,
'obj_name': 'ns0:ToolsConfigInfo'},
'numCPUs': 2}""" % instance_uuid
expected = re.sub(r'\s+', '', expected)
result = re.sub(r'\s+', '', repr(result))
self.assertEqual(expected, result)

View File

@ -51,11 +51,11 @@ def split_datastore_path(datastore_path):
return datastore_url, path.strip()
def get_vm_create_spec(client_factory, instance, data_store_name,
def get_vm_create_spec(client_factory, instance, name, data_store_name,
vif_infos, os_type="otherGuest"):
"""Builds the VM Create spec."""
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
config_spec.name = instance['uuid']
config_spec.name = name
config_spec.guestId = os_type
# Allow nested ESX instances to host 64 bit VMs.
@ -985,13 +985,14 @@ def get_vmdk_backed_disk_device(hardware_devices, uuid):
return device
def get_vmdk_volume_disk(hardware_devices):
def get_vmdk_volume_disk(hardware_devices, path=None):
if hardware_devices.__class__.__name__ == "ArrayOfVirtualDevice":
hardware_devices = hardware_devices.VirtualDevice
for device in hardware_devices:
if (device.__class__.__name__ == "VirtualDisk"):
return device
if not path or path == device.backing.fileName:
return device
def get_res_pool_ref(session, cluster, node_mo_id):

View File

@ -172,7 +172,8 @@ class VMwareVMOps(object):
LOG.debug(_("Deleted the datastore file"), instance=instance)
def spawn(self, context, instance, image_meta, injected_files,
admin_password, network_info, block_device_info=None):
admin_password, network_info, block_device_info=None,
instance_name=None):
"""
Creates a VM instance.
@ -283,9 +284,13 @@ class VMwareVMOps(object):
vif_infos = _get_vif_infos()
# Get the instance name. In some cases this may differ from the 'uuid',
# for example when the spawn of a rescue instance takes place.
if not instance_name:
instance_name = instance['uuid']
# Get the create vm config spec
config_spec = vm_util.get_vm_create_spec(
client_factory, instance,
client_factory, instance, instance_name,
data_store_name, vif_infos, os_type)
def _execute_create_vm():
@ -301,7 +306,7 @@ class VMwareVMOps(object):
LOG.debug(_("Created VM on the ESX host"), instance=instance)
_execute_create_vm()
vm_ref = vm_util.get_vm_ref(self._session, instance)
vm_ref = vm_util.get_vm_ref_from_name(self._session, instance_name)
# Set the machine.id parameter of the instance to inject
# the NIC configuration inside the VM
@ -895,15 +900,20 @@ class VMwareVMOps(object):
except Exception as exc:
LOG.exception(exc, instance=instance)
def destroy(self, instance, network_info, destroy_disks=True):
def destroy(self, instance, network_info, destroy_disks=True,
instance_name=None):
"""
Destroy a VM instance. Steps followed are:
1. Power off the VM, if it is in poweredOn state.
2. Un-register a VM.
3. Delete the contents of the folder holding the VM related data.
"""
# Get the instance name. In some cases this may differ from the 'uuid',
# for example when the spawn of a rescue instance takes place.
if not instance_name:
instance_name = instance['uuid']
try:
vm_ref = vm_util.get_vm_ref(self._session, instance)
vm_ref = vm_util.get_vm_ref_from_name(self._session, instance_name)
lst_properties = ["config.files.vmPathName", "runtime.powerState",
"datastore"]
props = self._session._call_method(vim_util,
@ -1028,9 +1038,10 @@ class VMwareVMOps(object):
self.power_off(instance)
r_instance = copy.deepcopy(instance)
r_instance['name'] = r_instance['name'] + self._rescue_suffix
r_instance['uuid'] = r_instance['uuid'] + self._rescue_suffix
instance_name = r_instance['uuid'] + self._rescue_suffix
self.spawn(context, r_instance, image_meta,
None, None, network_info)
None, None, network_info,
instance_name=instance_name)
# Attach vmdk to the rescue VM
hardware_devices = self._session._call_method(vim_util,
@ -1040,11 +1051,8 @@ class VMwareVMOps(object):
= vm_util.get_vmdk_path_and_adapter_type(hardware_devices)
# Figure out the correct unit number
unit_number = unit_number + 1
rescue_vm_ref = vm_util.get_vm_ref_from_uuid(self._session,
r_instance['uuid'])
if rescue_vm_ref is None:
rescue_vm_ref = vm_util.get_vm_ref_from_name(self._session,
r_instance['name'])
rescue_vm_ref = vm_util.get_vm_ref_from_name(self._session,
instance_name)
self._volumeops.attach_disk_to_vm(
rescue_vm_ref, r_instance,
adapter_type, disk_type, vmdk_path,
@ -1053,10 +1061,26 @@ class VMwareVMOps(object):
def unrescue(self, instance):
"""Unrescue the specified instance."""
# Get the original vmdk_path
vm_ref = vm_util.get_vm_ref(self._session, instance)
hardware_devices = self._session._call_method(vim_util,
"get_dynamic_property", vm_ref,
"VirtualMachine", "config.hardware.device")
vmdk_path, controller_key, adapter_type, disk_type, unit_number \
= vm_util.get_vmdk_path_and_adapter_type(hardware_devices)
r_instance = copy.deepcopy(instance)
instance_name = r_instance['uuid'] + self._rescue_suffix
r_instance['name'] = r_instance['name'] + self._rescue_suffix
r_instance['uuid'] = r_instance['uuid'] + self._rescue_suffix
self.destroy(r_instance, None)
# detach the original instance disk from the rescue disk
vm_rescue_ref = vm_util.get_vm_ref_from_name(self._session,
instance_name)
hardware_devices = self._session._call_method(vim_util,
"get_dynamic_property", vm_rescue_ref,
"VirtualMachine", "config.hardware.device")
device = vm_util.get_vmdk_volume_disk(hardware_devices, path=vmdk_path)
self._volumeops.detach_disk_from_vm(vm_rescue_ref, r_instance, device)
self.destroy(r_instance, None, instance_name=instance_name)
self._power_on(instance)
def power_off(self, instance):