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
parent dd569f0702
commit bfbf4b2cf1
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]
- Get compute ports vif ids mapping for the migration::
nsxadmin -r ports -o list (--property map-file=<filename>)
Config
~~~~~~

View File

@ -60,6 +60,7 @@ class ApiReplayCli(object):
ext_net_map=args.external_networks_map,
net_vni_map=args.networks_vni_map,
int_vni_map=args.internal_networks_vni_map,
vif_ids_map=args.vif_ids_map,
logfile=args.logfile,
max_retry=args.max_retry,
cert_file=args.cert_file)
@ -191,6 +192,10 @@ class ApiReplayCli(object):
"--internal-networks-vni-map",
help="Path to a json file mapping internal network ID "
"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(
"--max-retry",

View File

@ -57,7 +57,7 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
octavia_os_tenant_name, octavia_os_tenant_domain_id,
octavia_os_password, octavia_os_auth_url,
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
if neutron_conf:
@ -154,6 +154,13 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
else:
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.errors = []
@ -688,7 +695,8 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
port['id'])
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
port['network_id'] = network['id']

View File

@ -268,7 +268,8 @@ class PrepareObjectForMigration(object):
body.pop(field)
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)
body = self.drop_fields(port, self.drop_port_fields)
if remove_qos:
@ -296,6 +297,9 @@ class PrepareObjectForMigration(object):
body['port_security_enabled'] = False
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 'device_id' not in body:
body['device_id'] = ""

View File

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

View File

@ -1729,7 +1729,12 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
context, port_data)
device_owner = port_data.get('device_owner')
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
# MP ports won't be created for VMs before neutron sets the vif-id
vif_id = port_data['id']

View File

@ -52,19 +52,6 @@ def post_v2t_migration_cleanups(resource, event, trigger, **kwargs):
section['id'])
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
def migration_tier0_redistribute(resource, event, trigger, **kwargs):

View File

@ -15,6 +15,7 @@
import netaddr
from oslo_config import cfg
from oslo_log import log as logging
from oslo_serialization import jsonutils
from networking_l2gw.db.l2gateway import l2gateway_models
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
@ -33,6 +34,7 @@ from vmware_nsx.services.lbaas.nsx_v import lbaas_common as lb_common
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.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.nsxv.resources import utils
from vmware_nsx.shell import resources as shell
@ -333,6 +335,47 @@ def validate_config_for_migration(resource, event, trigger, **kwargs):
"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,
constants.NSX_MIGRATE_V_T,
shell.Operations.VALIDATE.value)
registry.subscribe(list_ports_vif_ids,
constants.PORTS,
shell.Operations.LIST.value)

View File

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