V2T migration: update network segment with vni

1. NSX|V admin utils: Add utility to list virtual wires
2. Add network vni field to the api_repaly extension
3. Let policy plugin set the vni value on the new segment
   while working in api-replay mode.

Change-Id: I872edd03cdd1a7ff1422cdc12ea2a1d75b5d0bcb
This commit is contained in:
asarfaty 2021-01-05 16:28:23 +02:00
parent 4e51ad25f5
commit 3e63441f62
11 changed files with 108 additions and 10 deletions

View File

@ -237,6 +237,12 @@ Portgroups
nsxadmin -r nsx-portgroups -o nsx-cleanup <--force>
VirtualWires
~~~~~~~~~~~~
- List all NSX virtual wires with their neutron id and vni::
nsxadmin -r nsx-virtualwires -o list (--property map-file=<filename>)
Security Groups, Firewall and Spoofguard
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -58,6 +58,7 @@ class ApiReplayCli(object):
octavia_os_auth_url=args.octavia_os_auth_url,
neutron_conf=args.neutron_conf,
ext_net_map=args.external_networks_map,
net_vni_map=args.networks_vni_map,
logfile=args.logfile,
max_retry=args.max_retry,
cert_file=args.cert_file)
@ -181,6 +182,10 @@ class ApiReplayCli(object):
"--external-networks-map",
help="Path to a json file mapping external network neutron ID "
"to tier0 ID.")
parser.add_argument(
"--networks-vni-map",
help="Path to a json file mapping neutron network ID to its "
"backend vni.")
parser.add_argument(
"--max-retry",

View File

@ -58,7 +58,7 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
octavia_os_username, octavia_os_user_domain_id,
octavia_os_tenant_name, octavia_os_tenant_domain_id,
octavia_os_password, octavia_os_auth_url,
neutron_conf, ext_net_map, logfile, max_retry,
neutron_conf, ext_net_map, net_vni_map, logfile, max_retry,
cert_file):
# Init config and logging
@ -133,6 +133,13 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
else:
self.ext_net_map = None
if net_vni_map:
with open(net_vni_map, 'r') as myfile:
data = myfile.read()
self.net_vni_map = jsonutils.loads(data)
else:
self.net_vni_map = None
LOG.info("Starting NSX migration to %s.", self.dest_plugin)
# Migrate all the objects
self.migrate_security_groups()
@ -508,7 +515,8 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
body = self.prepare_network(
network, remove_qos=remove_qos,
dest_default_public_net=dest_default_public_net,
dest_azs=dest_azs, ext_net_map=self.ext_net_map)
dest_azs=dest_azs, ext_net_map=self.ext_net_map,
net_vni_map=self.net_vni_map)
# only create network if the dest server doesn't have it
if self.have_id(network['id'], dest_networks):
@ -517,10 +525,11 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
continue
# Ignore internal NSXV objects
if network['project_id'] == nsxv_constants.INTERNAL_TENANT_ID:
LOG.info("Skip network %s: Internal NSX-V network",
network['id'])
continue
# TODO(asarfaty) - temporarily migrate those as well
# if network['project_id'] == nsxv_constants.INTERNAL_TENANT_ID:
# LOG.info("Skip network %s: Internal NSX-V network",
# network['id'])
# continue
try:
created_net = self.dest_neutron.create_network(

View File

@ -165,7 +165,7 @@ class PrepareObjectForMigration(object):
def prepare_network(self, net, dest_default_public_net=True,
remove_qos=False, dest_azs=None, direct_call=False,
ext_net_map=None):
ext_net_map=None, net_vni_map=None):
self.fix_description(net)
body = self.drop_fields(net, self.drop_network_fields)
@ -229,6 +229,10 @@ class PrepareObjectForMigration(object):
body['availability_zone_hints'] = []
elif direct_call:
body['availability_zone_hints'] = []
if net_vni_map and body['id'] in net_vni_map:
body['vni'] = net_vni_map[body['id']]
return body
def prepare_subnet(self, subnet, direct_call=False):

View File

@ -15,7 +15,9 @@
# under the License.
#
from neutron_lib.api import converters
from neutron_lib.api import extensions
from neutron_lib import constants
from neutron_lib.db import constants as db_const
@ -36,6 +38,10 @@ RESOURCE_ATTRIBUTE_MAP = {
},
'networks': {
'id': ID_WITH_POST,
'vni': {'allow_post': True, 'allow_put': False,
'default': constants.ATTR_NOT_SPECIFIED,
'convert_to': converters.convert_to_int_if_not_none,
'is_visible': True},
},
'subnets': {
'id': ID_WITH_POST,

View File

@ -600,8 +600,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
network['id'])
def _create_network_on_backend(self, context, net_data,
transparent_vlan,
provider_data, az):
transparent_vlan, provider_data, az,
request_data):
net_data['id'] = net_data.get('id') or uuidutils.generate_uuid()
# update the network name to indicate the neutron id too.
@ -641,6 +641,10 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
if az.use_policy_md:
kwargs['metadata_proxy_id'] = az._native_md_proxy_uuid
# Set the segment vni for nsx-v portgroups migration
if request_data.get('vni') and cfg.CONF.api_replay_mode:
kwargs['overlay_id'] = request_data['vni']
self.nsxpolicy.segment.create_or_overwrite(
net_name, **kwargs)
@ -767,7 +771,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
if is_backend_network:
try:
self._create_network_on_backend(
context, created_net, vlt, provider_data, az)
context, created_net, vlt, provider_data, az, net_data)
except Exception as e:
LOG.exception("Failed to create NSX network network: %s", e)
with excutils.save_and_reraise_exception():

View File

@ -521,6 +521,11 @@ class Vcns(object):
uri = '/api/2.0/vdn/virtualwires/%s' % virtualwire_id
return self.do_request(HTTP_DELETE, uri, format='xml')
def get_virtual_wires(self):
"""Deletes a virtual wire."""
uri = '/api/2.0/vdn/virtualwires'
return self.do_request(HTTP_GET, uri)
def create_port_group(self, dvs_id, request):
"""Creates a port group on a DVS

View File

@ -68,6 +68,7 @@ BGP_GW_EDGE = 'bgp-gw-edge'
ROUTING_REDIS_RULE = 'routing-redistribution-rule'
BGP_NEIGHBOUR = 'bgp-neighbour'
NSX_PORTGROUPS = 'nsx-portgroups'
NSX_VIRTUALWIRES = 'nsx-virtualwires'
NSX_MIGRATE_V_T = 'nsx-migrate-v2t'
# NSXTV only Resource Constants

View File

@ -43,6 +43,17 @@ def get_networks_from_backend():
return et.fromstring(so_list)
def get_virtual_wires():
"""Return a hash of the backend virtual wires by their id"""
nsxv = utils.get_nsxv_client()
h, res = nsxv.get_virtual_wires()
vw_list = res['dataPage']['data']
vw_hash = {}
for vw in vw_list:
vw_hash[vw['objectId']] = vw
return vw_hash
def get_networks():
"""Create an array of all the backend networks and their data
"""
@ -295,6 +306,41 @@ def get_dvs_id_from_backend_name(backend_name):
return reg.group(0)
@admin_utils.output_header
def list_nsx_virtual_wires(resource, event, trigger, **kwargs):
filename = None
if kwargs.get('property'):
properties = admin_utils.parse_multi_keyval_opt(kwargs['property'])
filename = properties.get('map-file')
vws = get_virtual_wires()
table_results = []
map_results = {}
admin_context = context.get_admin_context()
with utils.NsxVPluginWrapper() as plugin:
neutron_networks = plugin.get_networks(admin_context, fields=['id'])
for net in neutron_networks:
# get the nsx id:
net_morefs = nsx_db.get_nsx_switch_ids(admin_context.session,
net['id'])
for moref in net_morefs:
if not moref.startswith('virtualwire'):
continue
vni = vws.get(moref, {}).get('vdnId')
table_results.append({'neutron_id': net['id'],
'nsx_id': moref,
'vni': vni})
map_results[net['id']] = vni
LOG.info(formatters.output_formatter(constants.NSX_VIRTUALWIRES,
table_results,
['neutron_id', 'nsx_id', 'vni']))
if filename:
f = open(filename, "a")
f.write("%s" % map_results)
f.close()
LOG.info("Mapping data saved into %s", filename)
@admin_utils.output_header
def delete_backend_network(resource, event, trigger, **kwargs):
"""Delete a backend network by its moref
@ -367,3 +413,6 @@ registry.subscribe(list_nsx_portgroups,
registry.subscribe(delete_nsx_portgroups,
constants.NSX_PORTGROUPS,
shell.Operations.NSX_CLEAN.value)
registry.subscribe(list_nsx_virtual_wires,
constants.NSX_VIRTUALWIRES,
shell.Operations.LIST.value)

View File

@ -213,6 +213,8 @@ nsxv_resources = {
constants.NSX_PORTGROUPS: Resource(constants.NSX_PORTGROUPS,
[Operations.LIST.value,
Operations.NSX_CLEAN.value]),
constants.NSX_VIRTUALWIRES: Resource(constants.NSX_VIRTUALWIRES,
[Operations.LIST.value]),
constants.SECURITY_GROUPS: Resource(constants.SECURITY_GROUPS,
[Operations.LIST.value,
Operations.FIX_MISMATCH.value,

View File

@ -172,6 +172,13 @@ class FakeVcns(object):
response = {"interfaces": self._edges[edge_id].get('interfaces', [])}
return (header, response)
def get_virtual_wires(self):
header = {
'status': 200
}
response = {"dataPage": {"data": []}}
return (header, response)
def update_vdr_internal_interface(
self, edge_id, interface_index, interface):
header = {