admin utility to disconnect edges in nsx-v

Will be used for V2T migration and rollback

Change-Id: I698ebb8353735b82308312dc76562eb426e57b3f
This commit is contained in:
asarfaty 2020-12-07 08:53:44 +02:00 committed by Adit Sarfaty
parent 43f1444717
commit bcbc82bcbc
5 changed files with 200 additions and 24 deletions

View File

@ -79,6 +79,14 @@ Edges
nsxadmin -o nsx-update -r edges --property hostgroup=clean
- Disconnect edges interfaces (for V2T migration)::
nsxadmin -o nsx-disconnect -r edges
- Reconnect edges interfaces (for V2T migration rollback)::
nsxadmin -o nsx-reconnect -r edges
Orphaned Edges
~~~~~~~~~~~~~~

View File

@ -90,13 +90,16 @@ def get_lb_interface(context, plugin, lb_id, subnet_id):
def create_lb_interface(context, plugin, lb_id, subnet_id, tenant_id,
vip_addr=None, subnet=None):
vip_addr=None, subnet=None, internal=False):
if not subnet:
subnet = plugin.get_subnet(context, subnet_id)
network_id = subnet.get('network_id')
network = plugin.get_network(context.elevated(), network_id)
port_dict = {'name': 'lb_if-' + lb_id,
name = 'lb_if-' + lb_id
if internal:
name = "internal for V2T migration"
port_dict = {'name': name,
'admin_state_up': True,
'network_id': network_id,
'tenant_id': tenant_id,
@ -131,13 +134,14 @@ def create_lb_interface(context, plugin, lb_id, subnet_id, tenant_id,
network_id, address_groups)
def delete_lb_interface(context, plugin, lb_id, subnet_id):
def delete_lb_interface(context, plugin, lb_id, subnet_id, internal=False):
resource_id = get_lb_edge_name(context, lb_id)
subnet = plugin.get_subnet(context, subnet_id)
network_id = subnet.get('network_id')
lb_ports = get_lb_interface(context, plugin, lb_id, subnet_id)
for lb_port in lb_ports:
plugin.delete_port(context, lb_port['id'], allow_delete_lb_if=True)
plugin.delete_port(context, lb_port['id'], allow_delete_lb_if=True,
allow_delete_internal=internal)
edge_utils.delete_interface(plugin.nsx_v, context, resource_id, network_id,
dist=False)

View File

@ -16,34 +16,34 @@
import pprint
import textwrap
from neutron_lib.callbacks import registry
from neutron_lib import context as n_context
from neutron_lib import exceptions
from neutron_lib.exceptions import l3 as l3_exc
from oslo_config import cfg
from oslo_log import log as logging
from vmware_nsx.common import config
from vmware_nsx.common import nsxv_constants
from vmware_nsx.db import nsxv_db
from vmware_nsx.dvs import dvs
from vmware_nsx.plugins.nsx_v import availability_zones as nsx_az
from vmware_nsx.plugins.nsx_v.vshield.common import (
constants as vcns_const)
from vmware_nsx.plugins.nsx_v.vshield.common import (
exceptions as nsxv_exceptions)
from vmware_nsx.plugins.nsx_v.vshield import edge_utils
from vmware_nsx.plugins.nsx_v.vshield import vcns_driver
from vmware_nsx.services.lbaas.nsx_v import lbaas_common as lb_common
from vmware_nsx.shell.admin.plugins.common import constants
from vmware_nsx.shell.admin.plugins.common import formatters
import vmware_nsx.shell.admin.plugins.common.utils as admin_utils
import vmware_nsx.shell.admin.plugins.nsxv.resources.utils as utils
import vmware_nsx.shell.resources as shell
from neutron_lib.callbacks import registry
from neutron_lib import context as n_context
from neutron_lib import exceptions
from oslo_config import cfg
from oslo_log import log as logging
from vmware_nsx.common import nsxv_constants
from vmware_nsx.db import nsxv_db
from vmware_nsx.plugins.nsx_v import availability_zones as nsx_az
from vmware_nsx.plugins.nsx_v.vshield.common import (
constants as vcns_const)
import vmware_nsx.plugins.nsx_v.vshield.common.exceptions as nsxv_exceptions
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
LOG = logging.getLogger(__name__)
nsxv = utils.get_nsxv_client()
INTERNAL_SUBNET = "169.254"
@admin_utils.output_header
@ -644,6 +644,160 @@ def nsx_update_edges(resource, event, trigger, **kwargs):
"to update.", {'result': result, 'total': total})
def _update_edge_interface_conectivity(edge_id, interface, disconnect,
nsxv_manager, distributed,
router_admin_state_up):
# keep the 169.254 interfaces connected and disable all the rest
primary_ip = None
addr_grp = interface.get('addressGroups', {}).get('addressGroups', [])
if len(addr_grp):
primary_ip = addr_grp[0]['primaryAddress']
else:
# Skip interfaces with no IPs
if (not interface.get('subInterfaces') or
not interface['subInterfaces'].get('subInterfaces')):
LOG.debug("Interface %s has no addressGroups and will not be "
"modified", interface.get('name'))
return
# skip interfaces on 169.254 subnet
if primary_ip and primary_ip.startswith(INTERNAL_SUBNET):
LOG.debug("Interface %s is on the %s.0.0 network and will not be "
"modified", interface.get('name'), INTERNAL_SUBNET)
return
# skip interfaces that are already connected/disconnected
if ((disconnect and not interface['isConnected']) or
(not disconnect and interface['isConnected'])):
LOG.debug("Interface %s already %s: %s", interface.get('name'),
'disconnected' if disconnect else 'connected', interface)
return
# if the router admin state is down, skip internal interfaces
if not router_admin_state_up and interface.get('type') == 'internal':
LOG.debug("Skipping internal interface %s as router admin state "
"is down", interface.get('name'))
return
if interface.get('portgroupId') or interface.get('connectedToId'):
# Its an active interface
interface['isConnected'] = not disconnect
try:
if distributed:
nsxv_manager.vcns.update_vdr_internal_interface(
edge_id, interface['index'], {'interface': interface})
else:
nsxv_manager.vcns.update_interface(edge_id, interface)
LOG.info("%s interface %s of %s %s",
'Disconneted' if disconnect else 'Connected',
interface['name'], edge_id,
'(distributed)' if distributed else '')
except Exception as e:
LOG.error("Failed with %s", e)
def _update_edges_connectivity(disconnect=True):
edges = utils.get_nsxv_backend_edges()
context = n_context.get_admin_context()
internal_subnet = None
with utils.NsxVPluginWrapper() as plugin:
nsxv_manager = vcns_driver.VcnsDriver(
edge_utils.NsxVCallbacks(plugin))
for edge in edges:
edge_id = edge.get('id')
# check that this is a neutron edge
bindings = nsxv_db.get_nsxv_router_binding_by_edge(
context.session, edge_id)
if not bindings:
LOG.info("Skipping non-neutron %s %s",
edge_id, edge.get('name'))
continue
router_id = bindings.router_id
router_admin_state_up = True
try:
rtr_obj = plugin.get_router(context, router_id)
except l3_exc.RouterNotFound:
pass
else:
router_admin_state_up = rtr_obj.get('admin_state_up', True)
# skip backup edges
if not edge.get('name') or edge['name'].startswith('backup-'):
LOG.info("Skipping backup %s %s",
edge_id, edge['name'])
continue
LOG.error("%s interfaces of %s %s",
'Disconnecting' if disconnect else 'Connecting',
edge_id, edge['name'])
if edge.get('type') == 'distributedRouter':
header, response = nsxv_manager.vcns.get_edge_interfaces(
edge_id)
for interface in response['interfaces']:
_update_edge_interface_conectivity(
edge_id, interface, disconnect, nsxv_manager,
True, router_admin_state_up)
elif edge['name'].startswith('lbaas-'):
# if the uplink interface has no ip - keep it, and disconnect
# all the rest.
# else - add/delete a dummy interface and disconnect all others
uplink_ip = None
h, uplink_if = nsxv_manager.vcns.query_interface(edge_id, 0)
if uplink_if:
addr_grp = uplink_if.get('addressGroups', {}).get(
'addressGroups', [])
uplink_ip = None
if len(addr_grp):
uplink_ip = addr_grp[0]['primaryAddress']
if uplink_ip:
# create/delete an interface on the inter-edge network
if not internal_subnet:
int_net = nsxv_db.get_nsxv_internal_network_for_az(
context.session, "inter_edge_net", "default")
subnets = plugin.get_subnets(
context, fields=['id'],
filters={'network_id': [int_net.network_id]})
internal_subnet = subnets[0]['id']
lb_id = edge['name'][6:]
if disconnect:
lb_common.create_lb_interface(
context, plugin, lb_id, internal_subnet,
"internal", internal=True)
header, response = nsxv_manager.vcns.get_interfaces(edge_id)
for interface in response['vnics']:
if not uplink_ip and interface['index'] == 0:
# change the connectivity of the uplink only if it
# does not have ip
continue
_update_edge_interface_conectivity(
edge_id, interface, disconnect, nsxv_manager,
False, router_admin_state_up)
if uplink_ip and not disconnect:
# Remove the dummy interface
lb_common.delete_lb_interface(context, plugin, lb_id,
internal_subnet,
internal=True)
else:
header, response = nsxv_manager.vcns.get_interfaces(edge_id)
for interface in response['vnics']:
_update_edge_interface_conectivity(
edge_id, interface, disconnect, nsxv_manager,
False, router_admin_state_up)
@admin_utils.output_header
def nsx_disconnect_edges(resource, event, trigger, **kwargs):
return _update_edges_connectivity(disconnect=True)
@admin_utils.output_header
def nsx_reconnect_edges(resource, event, trigger, **kwargs):
return _update_edges_connectivity(disconnect=False)
registry.subscribe(nsx_list_edges,
constants.EDGES,
shell.Operations.NSX_LIST.value)
@ -671,3 +825,9 @@ registry.subscribe(list_orphaned_router_bindings,
registry.subscribe(clean_orphaned_router_bindings,
constants.ORPHANED_BINDINGS,
shell.Operations.CLEAN.value)
registry.subscribe(nsx_disconnect_edges,
constants.EDGES,
shell.Operations.NSX_DISCONNECT.value)
registry.subscribe(nsx_reconnect_edges,
constants.EDGES,
shell.Operations.NSX_RECONNECT.value)

View File

@ -60,6 +60,8 @@ class Operations(enum.Enum):
NSX_RECREATE = 'nsx-recreate'
NSX_REDISTRIBURE = 'nsx-redistribute'
NSX_REORDER = 'nsx-reorder'
NSX_DISCONNECT = 'nsx-disconnect'
NSX_RECONNECT = 'nsx-reconnect'
NSX_TAG_DEFAULT = 'nsx-tag-default'
NSX_MIGRATE_V_V3 = 'nsx-migrate-v-v3'
MIGRATE_TO_POLICY = 'migrate-to-policy'
@ -168,7 +170,9 @@ nsxv_resources = {
[Operations.NSX_LIST.value,
Operations.NEUTRON_LIST.value,
Operations.NSX_UPDATE.value,
Operations.NSX_UPDATE_ALL.value]),
Operations.NSX_UPDATE_ALL.value,
Operations.NSX_DISCONNECT.value,
Operations.NSX_RECONNECT.value]),
constants.BACKUP_EDGES: Resource(constants.BACKUP_EDGES,
[Operations.LIST.value,
Operations.CLEAN.value,

View File

@ -204,7 +204,7 @@ class FakeVcns(object):
header = {
'status': 200
}
response = ''
response = {'vnics': {}}
return (header, response)
def update_interface(self, edge_id, vnic):