From 2a4ff322eb215da25c995d8bbe54aa7c7b31f104 Mon Sep 17 00:00:00 2001 From: Adit Sarfaty Date: Mon, 11 Sep 2017 16:14:13 +0300 Subject: [PATCH] NSX|V AdminUtil handle orphaned router vnics If deleting a router interface in the backend fails, the neutron port is deleted, but the NSX backend interface and the vnic DB entry are not deleted. This new admin utility will list and clean those. Change-Id: I002cac9c04f844c798097cf79d31dcefdea976ed --- doc/source/admin_util.rst | 11 ++ .../shell/admin/plugins/common/constants.py | 1 + .../admin/plugins/nsxv/resources/routers.py | 100 ++++++++++++++++++ vmware_nsx/shell/resources.py | 3 + 4 files changed, 115 insertions(+) diff --git a/doc/source/admin_util.rst b/doc/source/admin_util.rst index de5a2eaca9..d65e766feb 100644 --- a/doc/source/admin_util.rst +++ b/doc/source/admin_util.rst @@ -98,6 +98,17 @@ Orphaned Router bindings nsxadmin -r orphaned-bindings -o clean +Orphaned Router VNICs +~~~~~~~~~~~~~~~~~~~~~ + +- List orphaned router vnic entries (exist on the edge vnics bindings DB table, but the neutron interface port behind them is missing):: + + nsxadmin -r orphaned-vnics -o list + +- Clean orphaned router vnics (delete DB entry, and NSX router interface):: + + nsxadmin -r orphaned-vnics -o clean + Missing Edges ~~~~~~~~~~~~~ diff --git a/vmware_nsx/shell/admin/plugins/common/constants.py b/vmware_nsx/shell/admin/plugins/common/constants.py index e05c1d7537..0f199b538d 100644 --- a/vmware_nsx/shell/admin/plugins/common/constants.py +++ b/vmware_nsx/shell/admin/plugins/common/constants.py @@ -49,6 +49,7 @@ SPOOFGUARD_POLICY = 'spoofguard-policy' BACKUP_EDGES = 'backup-edges' ORPHANED_EDGES = 'orphaned-edges' ORPHANED_BINDINGS = 'orphaned-bindings' +ORPHANED_VNICS = 'orphaned-vnics' MISSING_EDGES = 'missing-edges' METADATA = 'metadata' MISSING_NETWORKS = 'missing-networks' diff --git a/vmware_nsx/shell/admin/plugins/nsxv/resources/routers.py b/vmware_nsx/shell/admin/plugins/nsxv/resources/routers.py index 92108891be..b4d31d16d1 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv/resources/routers.py +++ b/vmware_nsx/shell/admin/plugins/nsxv/resources/routers.py @@ -14,6 +14,7 @@ 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 @@ -186,6 +187,7 @@ def nsx_recreate_router_or_edge(resource, event, trigger, **kwargs): return nsx_recreate_router(router_id) +@admin_utils.output_header def migrate_distributed_routers_dhcp(resource, event, trigger, **kwargs): context = n_context.get_admin_context() nsxv = utils.get_nsxv_client() @@ -208,6 +210,96 @@ def migrate_distributed_routers_dhcp(resource, event, trigger, **kwargs): nsxv.update_routes(edge_id, route_obj) +@admin_utils.output_header +def list_orphaned_vnics(resource, event, trigger, **kwargs): + """List router orphaned router vnics where the port was deleted""" + orphaned_vnics = get_orphaned_vnics() + if not orphaned_vnics: + LOG.info("No orphaned router vnics found") + return + headers = ['edge_id', 'vnic_index', 'tunnel_index', 'network_id'] + LOG.info(formatters.output_formatter(constants.ORPHANED_VNICS, + orphaned_vnics, headers)) + + +def get_orphaned_vnics(): + orphaned_vnics = [] + context = n_context.get_admin_context() + vnic_binds = nsxv_db.get_edge_vnic_bindings_with_networks( + context.session) + with utils.NsxVPluginWrapper() as plugin: + for vnic_bind in vnic_binds: + edge_id = vnic_bind['edge_id'] + # check if this is a router edge by the router bindings table + router_bindings = nsxv_db.get_nsxv_router_bindings_by_edge( + context.session, edge_id) + if not router_bindings: + # Only log it. this is a different type of orphaned + LOG.warning("Router bindings for vnic %s not found", vnic_bind) + continue + + router_ids = [b['router_id'] for b in router_bindings] + routers = plugin.get_routers(context, + filters={'id': router_ids}) + if routers: + interface_found = False + # check if any of those routers is attached to this network + for router in routers: + if plugin._get_router_interface_ports_by_network( + context, router['id'], vnic_bind['network_id']): + interface_found = True + break + if not interface_found: + # for later deleting the interface we need to know if this + # is a distributed router. + # All the routers on the same edge are of the same type, + # so we can check the first one. + vnic_bind['distributed'] = routers[0].get('distributed') + orphaned_vnics.append(vnic_bind) + + return orphaned_vnics + + +@admin_utils.output_header +def clean_orphaned_vnics(resource, event, trigger, **kwargs): + """List router orphaned router vnics where the port was deleted""" + orphaned_vnics = get_orphaned_vnics() + if not orphaned_vnics: + LOG.info("No orphaned router vnics found") + return + headers = ['edge_id', 'vnic_index', 'tunnel_index', 'network_id'] + LOG.info(formatters.output_formatter(constants.ORPHANED_VNICS, + orphaned_vnics, headers)) + user_confirm = admin_utils.query_yes_no("Do you want to delete " + "orphaned vnics", + default="no") + if not user_confirm: + LOG.info("NSXv vnics deletion aborted by user") + return + + context = n_context.get_admin_context() + with utils.NsxVPluginWrapper() as plugin: + nsxv_manager = vcns_driver.VcnsDriver( + edge_utils.NsxVCallbacks(plugin)) + for vnic in orphaned_vnics: + if not vnic['distributed']: + try: + nsxv_manager.vcns.delete_interface( + vnic['edge_id'], vnic['vnic_index']) + except Exception as e: + LOG.error("Failed to delete vnic from NSX: %s", e) + nsxv_db.free_edge_vnic_by_network( + context.session, vnic['edge_id'], vnic['network_id']) + else: + try: + nsxv_manager.vcns.delete_vdr_internal_interface( + vnic['edge_id'], vnic['vnic_index']) + except Exception as e: + LOG.error("Failed to delete vnic from NSX: %s", e) + nsxv_db.delete_edge_vnic_binding_by_network( + context.session, vnic['edge_id'], vnic['network_id']) + + registry.subscribe(nsx_recreate_router_or_edge, constants.ROUTERS, shell.Operations.NSX_RECREATE.value) @@ -215,3 +307,11 @@ registry.subscribe(nsx_recreate_router_or_edge, registry.subscribe(migrate_distributed_routers_dhcp, constants.ROUTERS, shell.Operations.MIGRATE_VDR_DHCP.value) + +registry.subscribe(list_orphaned_vnics, + constants.ORPHANED_VNICS, + shell.Operations.NSX_LIST.value) + +registry.subscribe(clean_orphaned_vnics, + constants.ORPHANED_VNICS, + shell.Operations.NSX_CLEAN.value) diff --git a/vmware_nsx/shell/resources.py b/vmware_nsx/shell/resources.py index f2ebcb8dad..7b531a9538 100644 --- a/vmware_nsx/shell/resources.py +++ b/vmware_nsx/shell/resources.py @@ -186,6 +186,9 @@ nsxv_resources = { constants.ROUTERS: Resource(constants.ROUTERS, [Operations.NSX_RECREATE.value, Operations.MIGRATE_VDR_DHCP.value]), + constants.ORPHANED_VNICS: Resource(constants.ORPHANED_VNICS, + [Operations.NSX_LIST.value, + Operations.NSX_CLEAN.value]), constants.CONFIG: Resource(constants.CONFIG, [Operations.VALIDATE.value]), constants.BGP_GW_EDGE: Resource(constants.BGP_GW_EDGE,