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:
parent
7646c28315
commit
88861df2b9
|
@ -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})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue