TVD: update port migration for V -> T instances

The patch does the following:
1. set instance vNIC to a common network interface
2. Live migrates to T cluster
3. Updates the instance vNIC to opaque network

Example:
nsxadmin -r ports -o nsx-migrate-v-v3 \
    --property project-id=01dd52ff4c7047f79f6259f916c83790 \
    --property host-moref=host-11 --property respool-moref=resgroup-9 \
    --property datastore-moref=datastore-22 \
     --plugin nsxv3

There is also an option to use net-name. The default here is 'VM Network'

Change-Id: I24d9df3f7a3dbd11dffb86427367b809e2b49409
This commit is contained in:
Gary Kotton 2018-02-21 23:48:13 -08:00 committed by Adit Sarfaty
parent a1bfbe9256
commit f634145a1e
3 changed files with 102 additions and 11 deletions

View File

@ -347,7 +347,7 @@ Ports
- Update the VMs ports (all or of a specific project) on the backend after migrating nsx-v -> nsx-v3:: - Update the VMs ports (all or of a specific project) on the backend after migrating nsx-v -> nsx-v3::
nsxadmin -r ports -o nsx-migrate-v-v3 (--property project-id=<>) nsxadmin -r ports -o nsx-migrate-v-v3 (--property project-id=<> --property host-moref=<> --property respool-moref=<> --property net-name=<> --property datastore-moref=<>)) --plugin nsxv3
- Migrate exclude ports to use tags:: - Migrate exclude ports to use tags::

View File

@ -569,6 +569,27 @@ class VMManager(VCManagerBase):
LOG.error("Failed to reconfigure VM %(moref)s spec: %(e)s", LOG.error("Failed to reconfigure VM %(moref)s spec: %(e)s",
{'moref': vm_moref.value, 'e': e}) {'moref': vm_moref.value, 'e': e})
def _build_vm_spec_update(self, devices):
client_factory = self._session.vim.client.factory
vm_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
vm_spec.deviceChange = [devices]
return vm_spec
def update_vm_interface(self, vm_moref, devices):
update_spec = self._build_vm_spec_update(devices)
task = self._session.invoke_api(self._session.vim,
'ReconfigVM_Task',
vm_moref,
spec=update_spec)
try:
self._session.wait_for_task(task)
LOG.info("Updated VM moref %(moref)s spec - "
"attached an interface",
{'moref': vm_moref.value})
except Exception as e:
LOG.error("Failed to reconfigure VM %(moref)s spec: %(e)s",
{'moref': vm_moref.value, 'e': e})
def _build_vm_spec_detach(self, device): def _build_vm_spec_detach(self, device):
"""Builds the vif detach config spec.""" """Builds the vif detach config spec."""
# Code inspired by nova: get_network_detach_config_spec # Code inspired by nova: get_network_detach_config_spec
@ -622,6 +643,59 @@ class VMManager(VCManagerBase):
if port: if port:
self._update_port_security_policy(dvs_moref, port, status) self._update_port_security_policy(dvs_moref, port, status)
def update_vm_network(self, device, name='VM Network'):
# In order to live migrate need a common network for interfaces
client_factory = self._session.vim.client.factory
network_spec = client_factory.create('ns0:VirtualDeviceConfigSpec')
network_spec.operation = 'edit'
backing = client_factory.create(
'ns0:VirtualEthernetCardNetworkBackingInfo')
backing.deviceName = name
device.backing = backing
network_spec.device = device
return network_spec
def update_vm_opaque_spec(self, vif_info, device):
"""Updates the backing for the VIF spec."""
client_factory = self._session.vim.client.factory
network_spec = client_factory.create('ns0:VirtualDeviceConfigSpec')
network_spec.operation = 'edit'
backing = client_factory.create(
'ns0:VirtualEthernetCardOpaqueNetworkBackingInfo')
backing.opaqueNetworkId = vif_info['nsx_id']
backing.opaqueNetworkType = 'nsx.LogicalSwitch'
# Configure externalId
device.externalId = vif_info['iface_id']
device.backing = backing
network_spec.device = device
return network_spec
def relocate_vm_spec(self, client_factory, respool_moref=None,
datastore_moref=None, host_moref=None,
disk_move_type="moveAllDiskBackingsAndAllowSharing"):
rel_spec = client_factory.create('ns0:VirtualMachineRelocateSpec')
if datastore_moref:
datastore = vim_util.get_moref(datastore_moref, 'Datastore')
else:
datastore = None
rel_spec.datastore = datastore
host = vim_util.get_moref(host_moref, 'HostSystem')
rel_spec.host = host
res_pool = vim_util.get_moref(respool_moref, 'ResourcePool')
rel_spec.pool = res_pool
return rel_spec
def relocate_vm(self, vm_ref, respool_moref=None, datastore_moref=None,
host_moref=None,
disk_move_type="moveAllDiskBackingsAndAllowSharing"):
client_factory = self._session.vim.client.factory
rel_spec = self.relocate_vm_spec(client_factory, respool_moref,
datastore_moref, host_moref,
disk_move_type)
task = self._session.invoke_api(self._session.vim, "RelocateVM_Task",
vm_ref, spec=rel_spec)
self._session.wait_for_task(task)
class ClusterManager(VCManagerBase): class ClusterManager(VCManagerBase):
"""Management class for Cluster related VC tasks.""" """Management class for Cluster related VC tasks."""

View File

@ -232,6 +232,17 @@ def migrate_compute_ports_vms(resource, event, trigger, **kwargs):
project = properties.get('project-id') project = properties.get('project-id')
if project: if project:
port_filters['project_id'] = [project] port_filters['project_id'] = [project]
net_name = properties.get('net-name', 'VM Network')
LOG.info("Common network name for migration %s", net_name)
host_moref = properties.get('host-moref')
# TODO(garyk): We can explore the option of passing the cluster
# moref then this will remove the need for the host-moref and the
# resource pool moref.
respool_moref = properties.get('respool-moref')
datastore_moref = properties.get('datastore-moref')
if not host_moref:
LOG.error("Unable to migrate with no host")
return
# Go over all the ports from the plugin # Go over all the ports from the plugin
admin_cxt = neutron_context.get_admin_context() admin_cxt = neutron_context.get_admin_context()
@ -266,22 +277,28 @@ def migrate_compute_ports_vms(resource, event, trigger, **kwargs):
LOG.info("No need to update the spec of vm %s", device_id) LOG.info("No need to update the spec of vm %s", device_id)
continue continue
# find the old interface by it's mac and delete it
device = get_vm_network_device(vm_mng, vm_moref, port['mac_address']) device = get_vm_network_device(vm_mng, vm_moref, port['mac_address'])
if device is None: if device is None:
LOG.warning("No device with MAC address %s exists on the VM", LOG.warning("No device with MAC address %s exists on the VM",
port['mac_address']) port['mac_address'])
continue continue
device_type = device.__class__.__name__
LOG.info("Detaching old interface from VM %s", device_id) # Update interface to be common network
vm_mng.detach_vm_interface(vm_moref, device) devices = [vm_mng.update_vm_network(device, name=net_name)]
LOG.info("Update instance %s to common network", device_id)
# add the new interface as OpaqueNetwork vm_mng.update_vm_interface(vm_moref, devices=devices)
LOG.info("Attaching new interface to VM %s", device_id) LOG.info("Migrate instance %s to host %s", device_id, host_moref)
nsx_net_id = get_network_nsx_id(admin_cxt.session, port['network_id']) vm_mng.relocate_vm(vm_moref, host_moref=host_moref,
vm_mng.attach_vm_interface(vm_moref, port['id'], port['mac_address'], datastore_moref=datastore_moref,
nsx_net_id, device_type) respool_moref=respool_moref)
LOG.info("Update instance %s to opaque network", device_id)
device = get_vm_network_device(vm_mng, vm_moref, port['mac_address'])
vif_info = {'nsx_id': get_network_nsx_id(admin_cxt.session,
port['network_id']),
'iface_id': port['id']}
devices = [vm_mng.update_vm_opaque_spec(vif_info, device)]
vm_mng.update_vm_interface(vm_moref, devices=devices)
LOG.info("Instance %s successfully migrated!", device_id)
def migrate_exclude_ports(resource, event, trigger, **kwargs): def migrate_exclude_ports(resource, event, trigger, **kwargs):