Merge "VMware: fix VM resize bug" into stable/havana

This commit is contained in:
Jenkins 2013-12-06 17:33:16 +00:00 committed by Gerrit Code Review
commit 8f007df84c
6 changed files with 107 additions and 24 deletions

View File

@ -662,7 +662,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
res = self.conn.get_console_output(self.instance)
self.assertNotEqual(0, len(res))
def _test_finish_migration(self, power_on):
def _test_finish_migration(self, power_on, resize_instance=False):
"""
Tests the finish_migration method on vmops
"""
@ -693,7 +693,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
disk_info=None,
network_info=None,
block_device_info=None,
resize_instance=False,
resize_instance=resize_instance,
image_meta=None,
power_on=power_on)
@ -705,6 +705,12 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
self.assertRaises(NotImplementedError,
self._test_finish_migration, power_on=False)
def test_confirm_migration(self):
self._create_vm()
self.assertRaises(NotImplementedError,
self.conn.confirm_migration, self.context,
self.instance, None)
def _test_finish_revert_migration(self, power_on):
"""
Tests the finish_revert_migration method on vmops
@ -1116,6 +1122,11 @@ class VMwareAPIVCDriverTestCase(VMwareAPIVMTestCase):
self._test_finish_migration(power_on=False)
self.assertEquals(False, self.power_on_called)
def test_finish_migration_power_on_resize(self):
self._test_finish_migration(power_on=True,
resize_instance=True)
self.assertEquals(True, self.power_on_called)
def test_finish_revert_migration_power_on(self):
self._test_finish_revert_migration(power_on=True)
self.assertEquals(True, self.power_on_called)
@ -1174,3 +1185,25 @@ class VMwareAPIVCDriverTestCase(VMwareAPIVMTestCase):
info = self.conn.get_info({'uuid': self.uuid,
'node': self.instance_node})
self._check_vm_info(info, power_state.RUNNING)
def test_migrate_disk_and_power_off(self):
def fake_update_instance_progress(context, instance, step,
total_steps):
pass
def fake_get_host_ref_from_name(dest):
return None
self._create_vm()
instance_type = {'name': 'fake', 'flavorid': 'fake_id'}
self.stubs.Set(self.conn._vmops, "_update_instance_progress",
fake_update_instance_progress)
self.stubs.Set(self.conn._vmops, "_get_host_ref_from_name",
fake_get_host_ref_from_name)
self.conn.migrate_disk_and_power_off(self.context, self.instance,
'fake_dest', instance_type,
None)
def test_confirm_migration(self):
self._create_vm()
self.conn.confirm_migration(self.context, self.instance, None)

View File

@ -221,6 +221,19 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
vm_util.get_datastore_ref_and_name,
fake_session(fake_objects))
def test_get_resize_spec(self):
fake_instance = {'id': 7, 'name': 'fake!',
'uuid': 'bda5fb9e-b347-40e8-8256-42397848cb00',
'vcpus': 2, 'memory_mb': 2048}
result = vm_util.get_vm_resize_spec(fake.FakeFactory(),
fake_instance)
expected = """{'memoryMB': 2048,
'numCPUs': 2,
'obj_name': 'ns0:VirtualMachineConfigSpec'}"""
expected = re.sub(r'\s+', '', expected)
result = re.sub(r'\s+', '', repr(result))
self.assertEqual(expected, result)
def test_get_cdrom_attach_config_spec(self):
result = vm_util.get_cdrom_attach_config_spec(fake.FakeFactory(),

View File

@ -446,26 +446,30 @@ class VMwareVCDriver(VMwareESXDriver):
Transfers the disk of a running instance in multiple phases, turning
off the instance before the end.
"""
return self._vmops.migrate_disk_and_power_off(context, instance,
dest, instance_type)
_vmops = self._get_vmops_for_compute_node(instance['node'])
return _vmops.migrate_disk_and_power_off(context, instance,
dest, instance_type)
def confirm_migration(self, migration, instance, network_info):
"""Confirms a resize, destroying the source VM."""
self._vmops.confirm_migration(migration, instance, network_info)
_vmops = self._get_vmops_for_compute_node(instance['node'])
_vmops.confirm_migration(migration, instance, network_info)
def finish_revert_migration(self, instance, network_info,
block_device_info=None, power_on=True):
"""Finish reverting a resize, powering back on the instance."""
self._vmops.finish_revert_migration(instance, network_info,
block_device_info, power_on)
_vmops = self._get_vmops_for_compute_node(instance['node'])
_vmops.finish_revert_migration(instance, network_info,
block_device_info, power_on)
def finish_migration(self, context, migration, instance, disk_info,
network_info, image_meta, resize_instance=False,
block_device_info=None, power_on=True):
"""Completes a resize, turning on the migrated instance."""
self._vmops.finish_migration(context, migration, instance, disk_info,
network_info, image_meta, resize_instance,
block_device_info, power_on)
_vmops = self._get_vmops_for_compute_node(instance['node'])
_vmops.finish_migration(context, migration, instance, disk_info,
network_info, image_meta, resize_instance,
block_device_info, power_on)
def live_migration(self, context, instance_ref, dest,
post_method, recover_method, block_migration=False,

View File

@ -329,6 +329,9 @@ class VirtualMachine(ManagedObject):
setting of the Virtual Machine object.
"""
try:
if not hasattr(val, 'deviceChange'):
return
if len(val.deviceChange) < 2:
return
@ -965,6 +968,10 @@ class FakeVim(object):
task_mdo = create_task(method, "success")
return task_mdo.obj
def _clone_vm(self, method, *args, **kwargs):
"""Fakes a VM clone."""
return self._just_return_task(method)
def _unregister_vm(self, method, *args, **kwargs):
"""Unregisters a VM from the Host System."""
vm_ref = args[0]
@ -1105,6 +1112,11 @@ class FakeVim(object):
elif attr_name == "UnregisterVM":
return lambda *args, **kwargs: self._unregister_vm(attr_name,
*args, **kwargs)
elif attr_name == "CloneVM_Task":
return lambda *args, **kwargs: self._clone_vm(attr_name,
*args, **kwargs)
elif attr_name == "Rename_Task":
return lambda *args, **kwargs: self._just_return_task(attr_name)
elif attr_name == "SearchDatastore_Task":
return lambda *args, **kwargs: self._search_ds(attr_name,
*args, **kwargs)

View File

@ -106,6 +106,14 @@ def get_vm_create_spec(client_factory, instance, data_store_name,
return config_spec
def get_vm_resize_spec(client_factory, instance):
"""Provides updates for a VM spec."""
resize_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
resize_spec.numCPUs = int(instance['vcpus'])
resize_spec.memoryMB = int(instance['memory_mb'])
return resize_spec
def create_controller_spec(client_factory, key, adapter_type="lsiLogic"):
"""
Builds a Config Spec for the LSI or Bus Logic Controller's addition
@ -483,7 +491,8 @@ def clone_vm_spec(client_factory, location,
clone_spec = client_factory.create('ns0:VirtualMachineCloneSpec')
clone_spec.location = location
clone_spec.powerOn = power_on
clone_spec.snapshot = snapshot
if snapshot:
clone_spec.snapshot = snapshot
clone_spec.template = template
return clone_spec
@ -494,7 +503,8 @@ def relocate_vm_spec(client_factory, datastore=None, host=None,
rel_spec = client_factory.create('ns0:VirtualMachineRelocateSpec')
rel_spec.datastore = datastore
rel_spec.diskMoveType = disk_move_type
rel_spec.host = host
if host:
rel_spec.host = host
return rel_spec

View File

@ -1106,7 +1106,7 @@ class VMwareVMOps(object):
self._power_on(instance)
def _get_orig_vm_name_label(self, instance):
return instance['name'] + '-orig'
return instance['uuid'] + '-orig'
def _update_instance_progress(self, context, instance, step, total_steps):
"""Update instance progress percent to reflect current step number
@ -1139,9 +1139,9 @@ class VMwareVMOps(object):
total_steps=RESIZE_TOTAL_STEPS)
vm_ref = vm_util.get_vm_ref(self._session, instance)
# Read the host_ref for the destination. If this is None then the
# VC will decide on placement
host_ref = self._get_host_ref_from_name(dest)
if host_ref is None:
raise exception.HostNotFound(host=dest)
# 1. Power off the instance
self.power_off(instance)
@ -1165,7 +1165,8 @@ class VMwareVMOps(object):
# Get the clone vm spec
ds_ref = vm_util.get_datastore_ref_and_name(
self._session, None, dest)[0]
self._session, self._cluster, host_ref,
datastore_regex=self._datastore_regex)[0]
client_factory = self._session._get_vim().client.factory
rel_spec = vm_util.relocate_vm_spec(client_factory, ds_ref, host_ref)
clone_spec = vm_util.clone_vm_spec(client_factory, rel_spec)
@ -1177,7 +1178,7 @@ class VMwareVMOps(object):
self._session._get_vim(),
"CloneVM_Task", vm_ref,
folder=vm_folder_ref,
name=instance['name'],
name=instance['uuid'],
spec=clone_spec)
self._session._wait_for_task(instance['uuid'], vm_clone_task)
LOG.debug(_("Cloned VM to host %s") % dest, instance=instance)
@ -1188,10 +1189,9 @@ class VMwareVMOps(object):
def confirm_migration(self, migration, instance, network_info):
"""Confirms a resize, destroying the source VM."""
instance_name = self._get_orig_vm_name_label(instance)
# Destroy the original VM.
vm_ref = vm_util.get_vm_ref_from_uuid(self._session, instance['uuid'])
if vm_ref is None:
vm_ref = vm_util.get_vm_ref_from_name(self._session, instance_name)
# Destroy the original VM. The vm_ref is via the instance_name
# and not the UUID
vm_ref = vm_util.get_vm_ref_from_name(self._session, instance_name)
if vm_ref is None:
LOG.debug(_("instance not present"), instance=instance)
return
@ -1201,7 +1201,7 @@ class VMwareVMOps(object):
destroy_task = self._session._call_method(
self._session._get_vim(),
"Destroy_Task", vm_ref)
self._session._wait_for_task(instance['uuid'], destroy_task)
self._session._wait_for_task(instance_name, destroy_task)
LOG.debug(_("Destroyed the VM"), instance=instance)
except Exception as excep:
LOG.warn(_("In vmwareapi:vmops:confirm_migration, got this "
@ -1235,6 +1235,16 @@ class VMwareVMOps(object):
network_info, image_meta, resize_instance=False,
block_device_info=None, power_on=True):
"""Completes a resize, turning on the migrated instance."""
if resize_instance:
client_factory = self._session._get_vim().client.factory
vm_ref = vm_util.get_vm_ref(self._session, instance)
vm_resize_spec = vm_util.get_vm_resize_spec(client_factory,
instance)
reconfig_task = self._session._call_method(
self._session._get_vim(),
"ReconfigVM_Task", vm_ref,
spec=vm_resize_spec)
# 4. Start VM
if power_on:
self._power_on(instance)
@ -1442,8 +1452,9 @@ class VMwareVMOps(object):
"HostSystem", ["name"])
vm_util._cancel_retrieve_if_necessary(self._session, host_objs)
for host in host_objs:
if host.propSet[0].val == host_name:
return host.obj
if hasattr(host, 'propSet'):
if host.propSet[0].val == host_name:
return host.obj
return None
def _get_vmfolder_ref(self):