Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions nova/tests/unit/virt/vmwareapi/test_driver_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2708,10 +2708,13 @@ def test_live_migration(self, volumes_to_devices,

@mock.patch.object(volumeops.VMwareVolumeOps,
'map_volumes_to_devices', returns=[])
@mock.patch.object(vmops.VMwareVMOps,
'reconfigure_vm_device_change')
@mock.patch.object(vmops.VMwareVMOps,
'live_migration', side_effect=test.TestingException)
def test_live_migration_failure_rollback(self,
mock_live_migration,
mock_reconfigure_vm_device_change,
volumes_to_devices):
self._create_instance()
migrate_data = self._create_live_migrate_data()
Expand All @@ -2732,6 +2735,7 @@ def test_live_migration_failure_rollback(self,
migrate_data=migrate_data)

post_method.assert_not_called()
mock_reconfigure_vm_device_change.assert_called()
recover_method.assert_called()

def test_rollback_live_migration_at_destination(self):
Expand Down
4 changes: 1 addition & 3 deletions nova/tests/unit/virt/vmwareapi/test_vmops.py
Original file line number Diff line number Diff line change
Expand Up @@ -3179,9 +3179,7 @@ def fake_invoke(module, method, *args, **kwargs):
file_path)
self.assertEqual('fira-host', result[0])
cookies = result[1]
self.assertEqual(1, len(cookies))
self.assertEqual('vmware_cgi_ticket', cookies[0].name)
self.assertEqual('"fira-ticket"', cookies[0].value)
self.assertEqual('vmware_cgi_ticket="fira-ticket"', cookies)

def test_fetch_vsphere_image(self):
vsphere_location = 'vsphere://my?dcPath=mycenter&dsName=mystore'
Expand Down
59 changes: 12 additions & 47 deletions nova/virt/vmwareapi/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
A connection to the VMware vCenter platform.
"""
import contextlib
from operator import attrgetter
import os
import random
import re
Expand Down Expand Up @@ -173,9 +172,11 @@ def __init__(self, virtapi, scheme="https"):
# Register the OpenStack extension
self._register_openstack_extension()

client_factory = self._session.vim.client.factory

virtapi._compute.additional_endpoints.extend([
special_spawning._SpecialVmSpawningServer(self),
VmwareRpcService(self._vmops)])
VmwareRpcService(self._vmops, client_factory)])

def _check_min_version(self):
min_version = v_utils.convert_version_to_int(constants.MIN_VC_VERSION)
Expand Down Expand Up @@ -846,6 +847,8 @@ def check_can_live_migrate_source(self, context, instance,
if dest_check_data.instance_already_migrated:
return dest_check_data

self._vmops.check_can_live_migrate_source(instance)

if dest_check_data.is_same_vcenter:
# Drop the service-credentials, no need to check the volumes,
# as we do not need to mess around with them
Expand Down Expand Up @@ -873,52 +876,9 @@ def pre_live_migration(self, context, instance, block_device_info,
if migrate_data.instance_already_migrated:
return migrate_data

return self._pre_live_migration(context, instance,
return self._vmops.pre_live_migration(context, instance,
block_device_info, network_info, disk_info, migrate_data)

def _pre_live_migration(self, context, instance, block_device_info,
network_info, disk_info, migrate_data):
result = self._vmops.place_vm(context, instance)

if hasattr(result, 'drsFault'):
LOG.error("Placement Error: %s", vim_util.serialize_object(
result.drsFault, typed=True), instance=instance)

if (not hasattr(result, 'recommendations') or
not result.recommendations):
raise exception.MigrationError(
reason="PlaceVM did not give any recommendations")

rs = sorted([r for r in result.recommendations
if r.reason == "xvmotionPlacement" and
r.action],
key=attrgetter("rating"))
if not rs:
raise exception.MigrationError(
reason="Did not get any xvmotionPlacement")

relocate_spec = rs[0].action[0].relocateSpec

# Should never happen, but if it does we rather want an error
# here, than sometime down the line
if not relocate_spec.host:
raise exception.MigrationError(
reason="No host with enough resources")

# Samere here: Should never happen
if not relocate_spec.datastore:
raise exception.MigrationError(
reason="No datastore with enough resources")

# relocate_defaults are serialized/deserialized on put/get
defaults = migrate_data.relocate_defaults
spec = vim_util.serialize_object(relocate_spec, typed=True)
defaults["relocate_spec"] = spec
# Writing the values back
migrate_data.relocate_defaults = defaults

return migrate_data

def _get_checked_volumes(self, context, instance, required_values,
exc=exception.MigrationError):
volumes = self._get_volume_mappings(context, instance)
Expand Down Expand Up @@ -946,6 +906,7 @@ def live_migration(self, context, instance, dest,
post_method(context, instance, dest, block_migration, migrate_data)
return

original_cdroms = []
try:
# We require the target-datastore for all volume-attachment
required_volume_attributes = ["datastore_ref"]
Expand All @@ -971,12 +932,16 @@ def live_migration(self, context, instance, dest,
vif_model = None # Doesn't matter as we won't change the type
migrate_data.vif_infos = target.get_vif_info(context,
vif_model=vif_model, network_info=dest_network_info)
self._vmops.live_migration(instance, migrate_data, volumes)

self._vmops.live_migration(context, instance, migrate_data,
volumes, original_cdroms)
LOG.info("Migration operation completed", instance=instance)
post_method(context, instance, dest, block_migration, migrate_data)
except Exception:
LOG.exception("Failed due to an exception", instance=instance)
with excutils.save_and_reraise_exception():
self._vmops.reconfigure_vm_device_change(context, instance,
original_cdroms)
# We are still in the task-state migrating, so cannot
# recover the DRS settings. We rely on the sync to do that
LOG.debug("Calling live migration recover_method "
Expand Down
52 changes: 51 additions & 1 deletion nova/virt/vmwareapi/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,33 @@ def confirm_migration_destination(self, ctxt, instance=None):
return self._call('confirm_migration_destination', ctxt,
instance_uuid=instance.uuid)

def prepare_ds_transfer(self, ctxt, request_method=None,
ds_ref=None, path=None):
ds_ref_value = vutil.get_moref_value(ds_ref)
return self._call('prepare_ds_transfer', ctxt,
request_method=request_method,
ds_ref_value=ds_ref_value, path=path)

def delete_config_drive_files(self, ctxt, instance=None, cdroms=None):
serialized = [vutil.serialize_object(spec, True) for spec in cdroms]
return self._call('delete_config_drive_files', ctxt,
instance_uuid=instance.uuid,
cdroms=serialized)

def reconfigure_vm_device_change(self, ctxt, instance=None, devices=None):
serialized = [vutil.serialize_object(spec, True) for spec in devices]
return self._call('reconfigure_vm_device_change', ctxt,
instance_uuid=instance.uuid,
devices=serialized)


@profiler.trace_cls("rpc")
class VmwareRpcService(object):
target = messaging.Target(version=_VERSION, namespace=_NAMESPACE)

def __init__(self, vm_ops):
def __init__(self, vm_ops, client_factory):
self._vm_ops = vm_ops
self._client_factory = client_factory

def get_vif_info(self, ctxt, vif_model=None, network_info=None):
return self._vm_ops.get_vif_info(ctxt, vif_model=vif_model,
Expand Down Expand Up @@ -112,3 +132,33 @@ def rollback_migrate_disk(self, ctxt, instance_uuid=None,
def confirm_migration_destination(self, ctxt, instance_uuid=None):
instance = Instance.get_by_uuid(ctxt, instance_uuid, expected_attrs=[])
return self._vm_ops.confirm_migration_destination(ctxt, instance)

def prepare_ds_transfer(self, ctxt, request_method=None,
ds_ref_value=None, path=None):
ds_ref = vutil.get_moref(ds_ref_value, 'Datastore')
return self._vm_ops.prepare_ds_transfer(ctxt,
request_method=request_method,
ds_ref=ds_ref,
path=path)

def delete_config_drive_files(self, ctxt, instance_uuid=None,
cdroms=None):
instance = Instance.get_by_uuid(ctxt, instance_uuid, expected_attrs=[])
cf = self._client_factory
deserialized = [
vutil.deserialize_object(cf, spec, "VirtualDeviceConfigSpec")
for spec in cdroms]

return self._vm_ops.delete_config_drive_files(ctxt,
instance=instance, cdroms=deserialized)

def reconfigure_vm_device_change(self, ctxt, instance_uuid=None,
devices=None):
instance = Instance.get_by_uuid(ctxt, instance_uuid, expected_attrs=[])
cf = self._client_factory
deserialized = [
vutil.deserialize_object(cf, spec, "VirtualDeviceConfigSpec")
for spec in devices]

return self._vm_ops.reconfigure_vm_device_change(ctxt,
instance=instance, devices=deserialized)
33 changes: 21 additions & 12 deletions nova/virt/vmwareapi/vm_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,8 @@ def get_cdrom_attach_config_spec(client_factory,
datastore,
file_path,
controller_key,
cdrom_unit_number):
cdrom_unit_number,
cdrom_key=-1):
"""Builds and returns the cdrom attach config spec."""
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')

Expand All @@ -802,7 +803,8 @@ def get_cdrom_attach_config_spec(client_factory,
datastore,
controller_key,
file_path,
cdrom_unit_number)
cdrom_unit_number,
cdrom_key=cdrom_key)

device_config_spec.append(virtual_device_config_spec)

Expand Down Expand Up @@ -880,7 +882,8 @@ def get_hardware_devices_by_type(session, vm_ref):
nics = {}
controllers = {}
disks = {}
swap = []
cdroms = {}
swap = None
ephemeral = []
first_device = None

Expand All @@ -902,7 +905,7 @@ def get_hardware_devices_by_type(session, vm_ref):
backing_type = device.backing.__class__.__name__
if backing_type == "VirtualDiskFlatVer2BackingInfo":
if "swap" in device.backing.fileName:
swap.append(device)
swap = device
elif "ephemeral" in device.backing.fileName:
ephemeral.append(device)
else:
Expand All @@ -917,13 +920,16 @@ def get_hardware_devices_by_type(session, vm_ref):
disks[uuid.lower()] = device
elif backing_type == "VirtualDiskRawDiskMappingVer1BackingInfo":
disks[device.backing.lunUuid.lower()] = device
elif device_type == "VirtualCdrom":
cdroms[int(device.key)] = device

return {
"nics": nics,
"controllers": controllers,
"swap": swap,
"ephemeral": ephemeral,
"disks": disks,
"cdroms": cdroms,
"root_device": first_device
}

Expand Down Expand Up @@ -1084,28 +1090,31 @@ def create_virtual_cdrom_spec(client_factory,
datastore,
controller_key,
file_path,
cdrom_unit_number):
cdrom_unit_number,
cdrom_key=-1,
cdrom_device_backing=None):
"""Builds spec for the creation of a new Virtual CDROM to the VM."""
config_spec = client_factory.create(
'ns0:VirtualDeviceConfigSpec')
config_spec.operation = "add"
config_spec.operation = "edit" if cdrom_key > -1 else "add"

cdrom = client_factory.create('ns0:VirtualCdrom')

cdrom_device_backing = client_factory.create(
'ns0:VirtualCdromIsoBackingInfo')
cdrom_device_backing.datastore = datastore
cdrom_device_backing.fileName = file_path
if not cdrom_device_backing and file_path:
cdrom_device_backing = client_factory.create(
'ns0:VirtualCdromIsoBackingInfo')
cdrom_device_backing.datastore = datastore
cdrom_device_backing.fileName = file_path

cdrom.backing = cdrom_device_backing
cdrom.controllerKey = controller_key
cdrom.unitNumber = cdrom_unit_number
cdrom.key = -1
cdrom.key = cdrom_key

connectable_spec = client_factory.create('ns0:VirtualDeviceConnectInfo')
connectable_spec.startConnected = True
connectable_spec.allowGuestControl = False
connectable_spec.connected = True
connectable_spec.connected = cdrom_device_backing is not None

cdrom.connectable = connectable_spec

Expand Down
Loading