[Admin-Utils] NSX-V3 upgrade vm ports after migration
After using api_replay to migrate the neutron data from NSX-V to NSX-T we need to update the VM ports to use OpaqueNetwork instead of DistributedVirtualPortgroup Usage: nsxadmin -r ports -o nsx-migrate-v-v3 Output example: Detaching old interface from VM ad02211d-25a1-4e0b-ab6e-ffee48c77077 Updated VM moref vm-59 spec - detached an interface Attaching new interface to VM ad02211d-25a1-4e0b-ab6e-ffee48c77077 Updated VM moref vm-59 spec - attached an interface Change-Id: Ie6b4c929257be9bed9701c9c2073a0e65cab9839
This commit is contained in:
parent
c3b287d4b8
commit
353be39d92
@ -174,6 +174,10 @@ Ports
|
||||
|
||||
nsxadmin -r ports -o list-mismatches
|
||||
|
||||
- Update the VMs ports on the backend after migrating nsx-v -> nsx-v3
|
||||
|
||||
nsxadmin -r ports -o nsx-migrate-v-v3
|
||||
|
||||
Security Groups
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -308,7 +308,17 @@ class DvsManager(object):
|
||||
{'net_id': net_id,
|
||||
'dvs': self._dvs_moref.value})
|
||||
|
||||
def get_vm_moref(self, instance_uuid):
|
||||
def get_portgroup_info(self, pg_moref):
|
||||
"""Get portgroup information."""
|
||||
# Expand the properties to collect on need basis.
|
||||
properties = ['name']
|
||||
pg_info = self._session.invoke_api(vim_util,
|
||||
'get_object_properties_dict',
|
||||
self._session.vim,
|
||||
pg_moref, properties)
|
||||
return pg_info
|
||||
|
||||
def get_vm_moref_obj(self, instance_uuid):
|
||||
"""Get reference to the VM.
|
||||
The method will make use of FindAllByUuid to get the VM reference.
|
||||
This method finds all VM's on the backend that match the
|
||||
@ -323,14 +333,105 @@ class DvsManager(object):
|
||||
vmSearch=True,
|
||||
instanceUuid=True)
|
||||
if vm_refs:
|
||||
return vm_refs[0].value
|
||||
return vm_refs[0]
|
||||
|
||||
def get_portgroup_info(self, pg_moref):
|
||||
"""Get portgroup information."""
|
||||
# Expand the properties to collect on need basis.
|
||||
properties = ['name']
|
||||
pg_info = self._session.invoke_api(vim_util,
|
||||
'get_object_properties_dict',
|
||||
def get_vm_moref(self, instance_uuid):
|
||||
"""Get reference to the VM.
|
||||
"""
|
||||
vm_ref = self.get_vm_moref_obj(instance_uuid)
|
||||
if vm_ref:
|
||||
return vm_ref.value
|
||||
|
||||
def get_vm_spec(self, vm_moref):
|
||||
vm_spec = self._session.invoke_api(vim_util,
|
||||
'get_object_properties',
|
||||
self._session.vim,
|
||||
pg_moref, properties)
|
||||
return pg_info
|
||||
vm_moref, ['network'])[0]
|
||||
return vm_spec
|
||||
|
||||
def _build_vm_spec_attach(self, neutron_port_id, port_mac,
|
||||
nsx_net_id, device_type):
|
||||
# Code inspired by nova: _create_vif_spec
|
||||
client_factory = self._session.vim.client.factory
|
||||
vm_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
device_change = client_factory.create('ns0:VirtualDeviceConfigSpec')
|
||||
device_change.operation = "add"
|
||||
|
||||
net_device = client_factory.create('ns0:' + device_type)
|
||||
net_device.key = -47
|
||||
net_device.addressType = "manual"
|
||||
# configure the neutron port id and mac
|
||||
net_device.externalId = neutron_port_id
|
||||
net_device.macAddress = port_mac
|
||||
net_device.wakeOnLanEnabled = True
|
||||
|
||||
backing = client_factory.create(
|
||||
'ns0:VirtualEthernetCardOpaqueNetworkBackingInfo')
|
||||
# configure the NSX network Id
|
||||
backing.opaqueNetworkId = nsx_net_id
|
||||
backing.opaqueNetworkType = "nsx.LogicalSwitch"
|
||||
net_device.backing = backing
|
||||
|
||||
connectable_spec = client_factory.create(
|
||||
'ns0:VirtualDeviceConnectInfo')
|
||||
connectable_spec.startConnected = True
|
||||
connectable_spec.allowGuestControl = True
|
||||
connectable_spec.connected = True
|
||||
|
||||
net_device.connectable = connectable_spec
|
||||
|
||||
device_change.device = net_device
|
||||
vm_spec.deviceChange = [device_change]
|
||||
|
||||
return vm_spec
|
||||
|
||||
def attach_vm_interface(self, vm_moref, neutron_port_id,
|
||||
port_mac, nsx_net_id, device_type):
|
||||
new_spec = self._build_vm_spec_attach(
|
||||
neutron_port_id, port_mac, nsx_net_id, device_type)
|
||||
task = self._session.invoke_api(self._session.vim,
|
||||
'ReconfigVM_Task',
|
||||
vm_moref,
|
||||
spec=new_spec)
|
||||
try:
|
||||
self._session.wait_for_task(task)
|
||||
LOG.info(_LI("Updated VM moref %(moref)s spec - "
|
||||
"attached an interface"),
|
||||
{'moref': vm_moref.value})
|
||||
except Exception as e:
|
||||
LOG.error(_LE("Failed to reconfigure VM %(moref)s spec: %(e)s"),
|
||||
{'moref': vm_moref.value, 'e': e})
|
||||
|
||||
def _build_vm_spec_detach(self, device):
|
||||
"""Builds the vif detach config spec."""
|
||||
# Code inspired by nova: get_network_detach_config_spec
|
||||
client_factory = self._session.vim.client.factory
|
||||
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
virtual_device_config = client_factory.create(
|
||||
'ns0:VirtualDeviceConfigSpec')
|
||||
virtual_device_config.operation = "remove"
|
||||
virtual_device_config.device = device
|
||||
config_spec.deviceChange = [virtual_device_config]
|
||||
return config_spec
|
||||
|
||||
def detach_vm_interface(self, vm_moref, device):
|
||||
new_spec = self._build_vm_spec_detach(device)
|
||||
task = self._session.invoke_api(self._session.vim,
|
||||
'ReconfigVM_Task',
|
||||
vm_moref,
|
||||
spec=new_spec)
|
||||
try:
|
||||
self._session.wait_for_task(task)
|
||||
LOG.info(_LI("Updated VM %(moref)s spec - detached an interface"),
|
||||
{'moref': vm_moref.value})
|
||||
except Exception as e:
|
||||
LOG.error(_LE("Failed to reconfigure vm moref %(moref)s: %(e)s"),
|
||||
{'moref': vm_moref.value, 'e': e})
|
||||
|
||||
def get_vm_interfaces_info(self, vm_moref):
|
||||
hardware_devices = self._session.invoke_api(vim_util,
|
||||
"get_object_property",
|
||||
self._session.vim,
|
||||
vm_moref,
|
||||
"config.hardware.device")
|
||||
return hardware_devices
|
||||
|
@ -17,10 +17,11 @@ import logging
|
||||
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
from vmware_nsx._i18n import _LI, _LW
|
||||
from vmware_nsx._i18n import _LE, _LI, _LW
|
||||
from vmware_nsx.common import exceptions as nsx_exc
|
||||
from vmware_nsx.db import db as nsx_db
|
||||
from vmware_nsx.db import nsx_models
|
||||
from vmware_nsx.dvs import dvs
|
||||
from vmware_nsx.nsxlib.v3 import resources
|
||||
from vmware_nsx.plugins.nsx_v3 import plugin
|
||||
from vmware_nsx.services.qos.common import utils as qos_utils
|
||||
@ -58,6 +59,20 @@ def get_port_nsx_id(session, neutron_id):
|
||||
pass
|
||||
|
||||
|
||||
def get_network_nsx_id(session, neutron_id):
|
||||
# get the nsx switch id from the DB mapping
|
||||
mappings = nsx_db.get_nsx_switch_ids(session, neutron_id)
|
||||
if not mappings or len(mappings) == 0:
|
||||
LOG.debug("Unable to find NSX mappings for neutron "
|
||||
"network %s.", neutron_id)
|
||||
# fallback in case we didn't find the id in the db mapping
|
||||
# This should not happen, but added here in case the network was
|
||||
# created before this code was added.
|
||||
return neutron_id
|
||||
else:
|
||||
return mappings[0]
|
||||
|
||||
|
||||
def get_port_and_profile_clients():
|
||||
_nsx_client = v3_utils.get_nsxv3_client()
|
||||
return (resources.LogicalPort(_nsx_client),
|
||||
@ -168,6 +183,83 @@ def list_missing_ports(resource, event, trigger, **kwargs):
|
||||
LOG.info(_LI("All internal ports verified on the NSX manager"))
|
||||
|
||||
|
||||
def get_vm_network_device(dvs_mng, vm_moref, mac_address):
|
||||
"""Return the network device with MAC 'mac_address'.
|
||||
|
||||
This code was inspired by Nova vif.get_network_device
|
||||
"""
|
||||
hardware_devices = dvs_mng.get_vm_interfaces_info(vm_moref)
|
||||
if hardware_devices.__class__.__name__ == "ArrayOfVirtualDevice":
|
||||
hardware_devices = hardware_devices.VirtualDevice
|
||||
for device in hardware_devices:
|
||||
if hasattr(device, 'macAddress'):
|
||||
if device.macAddress == mac_address:
|
||||
return device
|
||||
|
||||
|
||||
def migrate_compute_ports_vms(resource, event, trigger, **kwargs):
|
||||
"""Update the VMs ports on the backend after migrating nsx-v -> nsx-v3
|
||||
|
||||
After using api_replay to migrate the neutron data from NSX-V to NSX-T
|
||||
we need to update the VM ports to use OpaqueNetwork instead of
|
||||
DistributedVirtualPortgroup
|
||||
"""
|
||||
# Connect to the DVS manager, using the configuration parameters
|
||||
try:
|
||||
dvs_mng = dvs.DvsManager()
|
||||
except Exception as e:
|
||||
LOG.error(_LE("Cannot connect to the DVS: Please update the [dvs] "
|
||||
"section in the nsx.ini file: %s"), e)
|
||||
return
|
||||
|
||||
# Go over all the compute ports from the plugin
|
||||
plugin = PortsPlugin()
|
||||
admin_cxt = neutron_context.get_admin_context()
|
||||
port_filters = {'device_owner': ['compute:None']}
|
||||
neutron_ports = plugin.get_ports(admin_cxt, filters=port_filters)
|
||||
|
||||
for port in neutron_ports:
|
||||
device_id = port.get('device_id')
|
||||
|
||||
# get the vm moref & spec from the DVS
|
||||
vm_moref = dvs_mng.get_vm_moref_obj(device_id)
|
||||
vm_spec = dvs_mng.get_vm_spec(vm_moref)
|
||||
|
||||
# Go over the VM interfaces and check if it should be updated
|
||||
update_spec = False
|
||||
for prop in vm_spec.propSet:
|
||||
if (prop.name == 'network' and
|
||||
hasattr(prop.val, 'ManagedObjectReference')):
|
||||
for net in prop.val.ManagedObjectReference:
|
||||
if net._type == 'DistributedVirtualPortgroup':
|
||||
update_spec = True
|
||||
|
||||
if not update_spec:
|
||||
LOG.info(_LI("No need to update the spec of vm %s"), device_id)
|
||||
continue
|
||||
|
||||
# find the old interface by it's mac and delete it
|
||||
device = get_vm_network_device(dvs_mng, vm_moref, port['mac_address'])
|
||||
if device is None:
|
||||
LOG.warning(_LW("No device with MAC address %s exists on the VM"),
|
||||
port['mac_address'])
|
||||
continue
|
||||
device_type = device.__class__.__name__
|
||||
|
||||
LOG.info(_LI("Detaching old interface from VM %s"), device_id)
|
||||
dvs_mng.detach_vm_interface(vm_moref, device)
|
||||
|
||||
# add the new interface as OpaqueNetwork
|
||||
LOG.info(_LI("Attaching new interface to VM %s"), device_id)
|
||||
nsx_net_id = get_network_nsx_id(admin_cxt.session, port['network_id'])
|
||||
dvs_mng.attach_vm_interface(vm_moref, port['id'], port['mac_address'],
|
||||
nsx_net_id, device_type)
|
||||
|
||||
|
||||
registry.subscribe(list_missing_ports,
|
||||
constants.PORTS,
|
||||
shell.Operations.LIST_MISMATCHES.value)
|
||||
|
||||
registry.subscribe(migrate_compute_ports_vms,
|
||||
constants.PORTS,
|
||||
shell.Operations.NSX_MIGRATE_V_V3.value)
|
||||
|
@ -46,6 +46,7 @@ class Operations(enum.Enum):
|
||||
NSX_UPDATE_SECRET = 'nsx-update-secret'
|
||||
NSX_RECREATE = 'nsx-recreate'
|
||||
MIGRATE_TO_DYNAMIC_CRITERIA = 'migrate-to-dynamic-criteria'
|
||||
NSX_MIGRATE_V_V3 = 'nsx-migrate-v-v3'
|
||||
STATUS = 'status'
|
||||
|
||||
ops = [op.value for op in Operations]
|
||||
@ -73,7 +74,8 @@ nsxv3_resources = {
|
||||
constants.NETWORKS: Resource(constants.NETWORKS,
|
||||
[Operations.LIST_MISMATCHES.value]),
|
||||
constants.PORTS: Resource(constants.PORTS,
|
||||
[Operations.LIST_MISMATCHES.value]),
|
||||
[Operations.LIST_MISMATCHES.value,
|
||||
Operations.NSX_MIGRATE_V_V3.value]),
|
||||
constants.ROUTERS: Resource(constants.ROUTERS,
|
||||
[Operations.LIST_MISMATCHES.value]),
|
||||
constants.DHCP_BINDING: Resource(constants.DHCP_BINDING,
|
||||
|
Loading…
Reference in New Issue
Block a user