V2T migration: Set expected VIF id to compute ports

- Add an udmin utility to provide the mapping between compute ports
and vif-ids
- Add extension to the api replay mode to support setting it by the plugin
- Add the mapping file as a parameter to teh api replay and use it in the ports migration
- Remove post migration cleanup of ports

Change-Id: Icfd3ef9f8056ee9c602ac5e85345daa59309f602
This commit is contained in:
asarfaty 2021-02-25 08:58:34 +02:00 committed by Adit Sarfaty
parent 1748e7cb4b
commit 7b9fb7b6eb
9 changed files with 77 additions and 17 deletions

View File

@ -341,6 +341,9 @@ V2T migration
nsxadmin -r nsx-migrate-v2t -o validate [--property transit-network=<cidr>] [--property strict=true] nsxadmin -r nsx-migrate-v2t -o validate [--property transit-network=<cidr>] [--property strict=true]
- Get compute ports vif ids mapping for the migration::
nsxadmin -r ports -o list (--property map-file=<filename>)
Config Config
~~~~~~ ~~~~~~

View File

@ -60,6 +60,7 @@ class ApiReplayCli(object):
ext_net_map=args.external_networks_map, ext_net_map=args.external_networks_map,
net_vni_map=args.networks_vni_map, net_vni_map=args.networks_vni_map,
int_vni_map=args.internal_networks_vni_map, int_vni_map=args.internal_networks_vni_map,
vif_ids_map=args.vif_ids_map,
logfile=args.logfile, logfile=args.logfile,
max_retry=args.max_retry, max_retry=args.max_retry,
cert_file=args.cert_file) cert_file=args.cert_file)
@ -191,6 +192,10 @@ class ApiReplayCli(object):
"--internal-networks-vni-map", "--internal-networks-vni-map",
help="Path to a json file mapping internal network ID " help="Path to a json file mapping internal network ID "
"to its backend vni.") "to its backend vni.")
parser.add_argument(
"--vif-ids-map",
help="Path to a json file mapping compute ports ids to the "
"expected vif ids.")
parser.add_argument( parser.add_argument(
"--max-retry", "--max-retry",

View File

@ -56,7 +56,7 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
octavia_os_tenant_name, octavia_os_tenant_domain_id, octavia_os_tenant_name, octavia_os_tenant_domain_id,
octavia_os_password, octavia_os_auth_url, octavia_os_password, octavia_os_auth_url,
neutron_conf, ext_net_map, net_vni_map, int_vni_map, neutron_conf, ext_net_map, net_vni_map, int_vni_map,
logfile, max_retry, cert_file): vif_ids_map, logfile, max_retry, cert_file):
# Init config and logging # Init config and logging
if neutron_conf: if neutron_conf:
@ -153,6 +153,13 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
else: else:
self.int_vni_map = None self.int_vni_map = None
if vif_ids_map:
with open(vif_ids_map, 'r') as myfile:
data = myfile.read()
self.vif_ids_map = jsonutils.loads(data)
else:
self.vif_ids_map = None
self.n_errors = 0 self.n_errors = 0
self.errors = [] self.errors = []
@ -687,7 +694,8 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
port['id']) port['id'])
continue continue
body = self.prepare_port(port, remove_qos=remove_qos) body = self.prepare_port(port, remove_qos=remove_qos,
vif_ids_map=self.vif_ids_map)
# specify the network_id that we just created above # specify the network_id that we just created above
port['network_id'] = network['id'] port['network_id'] = network['id']

View File

@ -268,7 +268,8 @@ class PrepareObjectForMigration(object):
body.pop(field) body.pop(field)
return body return body
def prepare_port(self, port, remove_qos=False, direct_call=False): def prepare_port(self, port, remove_qos=False, vif_ids_map=None,
direct_call=False):
self.fix_description(port) self.fix_description(port)
body = self.drop_fields(port, self.drop_port_fields) body = self.drop_fields(port, self.drop_port_fields)
if remove_qos: if remove_qos:
@ -296,6 +297,9 @@ class PrepareObjectForMigration(object):
body['port_security_enabled'] = False body['port_security_enabled'] = False
body['security_groups'] = [] body['security_groups'] = []
if vif_ids_map and body['id'] in vif_ids_map:
body['vif_id'] = vif_ids_map[body['id']]
if direct_call: if direct_call:
if 'device_id' not in body: if 'device_id' not in body:
body['device_id'] = "" body['device_id'] = ""

View File

@ -35,6 +35,9 @@ ID_WITH_POST = {'allow_post': True, 'allow_put': False,
RESOURCE_ATTRIBUTE_MAP = { RESOURCE_ATTRIBUTE_MAP = {
'ports': { 'ports': {
'id': ID_WITH_POST, 'id': ID_WITH_POST,
'vif_id': {'allow_post': True, 'allow_put': False,
'default': '',
'is_visible': True},
}, },
'networks': { 'networks': {
'id': ID_WITH_POST, 'id': ID_WITH_POST,

View File

@ -1716,7 +1716,12 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
context, port_data) context, port_data)
device_owner = port_data.get('device_owner') device_owner = port_data.get('device_owner')
vif_id = None vif_id = None
if not device_owner or device_owner != l3_db.DEVICE_OWNER_ROUTER_INTF: if (cfg.CONF.api_replay_mode and device_owner and
device_owner.startswith('compute:') and port_data.get('vif_id')):
# During api-replay, migrated vm port should have this vif-id
vif_id = port_data['vif_id']
elif (not device_owner or
device_owner != l3_db.DEVICE_OWNER_ROUTER_INTF):
# Set vif_id even if no device owner so that auto-generated # Set vif_id even if no device owner so that auto-generated
# MP ports won't be created for VMs before neutron sets the vif-id # MP ports won't be created for VMs before neutron sets the vif-id
vif_id = port_data['id'] vif_id = port_data['id']

View File

@ -53,19 +53,6 @@ def post_v2t_migration_cleanups(resource, event, trigger, **kwargs):
section['id']) section['id'])
continue continue
# cleanup migrated DVS ports (belong to the edges that are not in use)
segments = nsxpolicy.segment.list()
for seg in segments:
# skip non-neutron segments
if not p_utils.is_neutron_resource(seg):
continue
ports = nsxpolicy.segment_port.list(seg['id'])
# find the non-neutron ports and delete them
for port in ports:
if not p_utils.is_neutron_resource(port):
nsxpolicy.segment_port.delete(seg['id'], port['id'])
LOG.error("Deleted migrated non-neutron port %s", port['id'])
@admin_utils.output_header @admin_utils.output_header
def migration_tier0_redistribute(resource, event, trigger, **kwargs): def migration_tier0_redistribute(resource, event, trigger, **kwargs):

View File

@ -17,6 +17,7 @@ import sys
import netaddr import netaddr
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from oslo_serialization import jsonutils
from networking_l2gw.db.l2gateway import l2gateway_models from networking_l2gw.db.l2gateway import l2gateway_models
from neutron.services.qos import qos_plugin from neutron.services.qos import qos_plugin
@ -37,6 +38,7 @@ from vmware_nsx.services.lbaas.nsx_v3.implementation import lb_utils
from vmware_nsx.services.lbaas.octavia import constants as oct_const from vmware_nsx.services.lbaas.octavia import constants as oct_const
from vmware_nsx.services.qos.nsx_v3 import pol_utils as qos_utils from vmware_nsx.services.qos.nsx_v3 import pol_utils as qos_utils
from vmware_nsx.shell.admin.plugins.common import constants from vmware_nsx.shell.admin.plugins.common import constants
from vmware_nsx.shell.admin.plugins.common import formatters
from vmware_nsx.shell.admin.plugins.common import utils as admin_utils from vmware_nsx.shell.admin.plugins.common import utils as admin_utils
from vmware_nsx.shell.admin.plugins.nsxv.resources import utils from vmware_nsx.shell.admin.plugins.nsxv.resources import utils
from vmware_nsx.shell import resources as shell from vmware_nsx.shell import resources as shell
@ -356,6 +358,47 @@ def validate_config_for_migration(resource, event, trigger, **kwargs):
"NSX-T.") "NSX-T.")
@admin_utils.output_header
def list_ports_vif_ids(resource, event, trigger, **kwargs):
filename = None
if kwargs.get('property'):
properties = admin_utils.parse_multi_keyval_opt(kwargs['property'])
filename = properties.get('map-file')
admin_context = n_context.get_admin_context()
table_results = []
map_results = {}
with utils.NsxVPluginWrapper() as plugin:
neutron_ports = plugin.get_ports(admin_context)
for port in neutron_ports:
# skip non compute ports
if (not port.get('device_owner').startswith(
nl_constants.DEVICE_OWNER_COMPUTE_PREFIX)):
continue
device_id = port.get('device_id')
port_id = port['id']
vnic_index = plugin._get_port_vnic_index(admin_context, port_id)
table_results.append({'neutron_id': port_id,
'instance_id': device_id,
'vnic_index': vnic_index})
if vnic_index is not None:
map_results[port_id] = '%s:%s' % (device_id, 4000 + vnic_index)
LOG.info(formatters.output_formatter(
"Compute ports VID IDs", table_results,
['neutron_id', 'instance_id', 'vnic_index']))
if filename:
f = open(filename, "w")
f.write("%s" % jsonutils.dumps(map_results))
f.close()
LOG.info("Mapping data saved into %s", filename)
registry.subscribe(validate_config_for_migration, registry.subscribe(validate_config_for_migration,
constants.NSX_MIGRATE_V_T, constants.NSX_MIGRATE_V_T,
shell.Operations.VALIDATE.value) shell.Operations.VALIDATE.value)
registry.subscribe(list_ports_vif_ids,
constants.PORTS,
shell.Operations.LIST.value)

View File

@ -256,6 +256,8 @@ nsxv_resources = {
Operations.DELETE.value]), Operations.DELETE.value]),
constants.NSX_MIGRATE_V_T: Resource(constants.NSX_MIGRATE_V_T, constants.NSX_MIGRATE_V_T: Resource(constants.NSX_MIGRATE_V_T,
[Operations.VALIDATE.value]), [Operations.VALIDATE.value]),
constants.PORTS: Resource(constants.PORTS,
[Operations.LIST.value]),
} }