From ea782b906628f0da8501989bb03c172dbdc3e638 Mon Sep 17 00:00:00 2001 From: Adit Sarfaty Date: Tue, 29 Aug 2017 09:00:08 +0300 Subject: [PATCH] NSX|v: Admin Util remove router binding orphaned entries New utilities to list/delete orphaned router binding entries, meaning entries that the object behind them (router, loadbalancer or network) does not exist on neutron. Change-Id: I8a239b9d33a4900e2e90035111899015d68d30bb --- doc/source/admin_util.rst | 11 ++ .../shell/admin/plugins/common/constants.py | 1 + .../admin/plugins/nsxv/resources/edges.py | 131 ++++++++++++++++++ vmware_nsx/shell/resources.py | 3 + 4 files changed, 146 insertions(+) diff --git a/doc/source/admin_util.rst b/doc/source/admin_util.rst index cc1105eb13..879f1b8a7d 100644 --- a/doc/source/admin_util.rst +++ b/doc/source/admin_util.rst @@ -87,6 +87,17 @@ Orphaned Edges nsxadmin -r orphaned-edges -o clean +Orphaned Router bindings +~~~~~~~~~~~~~~~~~~~~~~~~ + +- List orphaned router bindings entries (exist on the router bindings DB table, but the neutron object behind them (router, network, or loadbalancer) is missing):: + + nsxadmin -r orphaned-bindings -o list + +- Clean orphaned router bindings entries (delete DB entry):: + + nsxadmin -r orphaned-bindings -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 c916e77e81..e05c1d7537 100644 --- a/vmware_nsx/shell/admin/plugins/common/constants.py +++ b/vmware_nsx/shell/admin/plugins/common/constants.py @@ -48,6 +48,7 @@ EDGES = 'edges' SPOOFGUARD_POLICY = 'spoofguard-policy' BACKUP_EDGES = 'backup-edges' ORPHANED_EDGES = 'orphaned-edges' +ORPHANED_BINDINGS = 'orphaned-bindings' MISSING_EDGES = 'missing-edges' METADATA = 'metadata' MISSING_NETWORKS = 'missing-networks' diff --git a/vmware_nsx/shell/admin/plugins/nsxv/resources/edges.py b/vmware_nsx/shell/admin/plugins/nsxv/resources/edges.py index 7bd449e7c8..2904101022 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv/resources/edges.py +++ b/vmware_nsx/shell/admin/plugins/nsxv/resources/edges.py @@ -19,6 +19,7 @@ import textwrap from vmware_nsx.common import config from vmware_nsx.dvs import dvs from vmware_nsx.plugins.nsx_v.vshield import edge_utils +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 @@ -27,6 +28,7 @@ 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 @@ -80,6 +82,129 @@ def neutron_list_router_edge_bindings(resource, event, trigger, **kwargs): ['edge_id', 'router_id', 'availability_zone', 'status'])) +@admin_utils.output_header +def clean_orphaned_router_bindings(resource, event, trigger, **kwargs): + """Delete nsx router bindings entries without real objects behind them""" + orphaned_list = get_orphaned_router_bindings() + if not len(orphaned_list): + LOG.info("No orphaned Router bindings found.") + return + + LOG.info("Before delete; Orphaned Bindings:") + LOG.info(formatters.output_formatter( + constants.ORPHANED_BINDINGS, orphaned_list, + ['edge_id', 'router_id', 'availability_zone', 'status'])) + + if not kwargs.get('force'): + if len(orphaned_list): + user_confirm = admin_utils.query_yes_no("Do you want to delete " + "orphaned bindings", + default="no") + if not user_confirm: + LOG.info("NSXv Router bindings deletion aborted by user") + return + + edgeapi = utils.NeutronDbClient() + for binding in orphaned_list: + nsxv_db.delete_nsxv_router_binding( + edgeapi.context.session, binding.router_id) + + LOG.info("Deleted %s orphaned router bindings. You may need to check for " + "orphaned edges now.", len(orphaned_list)) + + +@admin_utils.output_header +def list_orphaned_router_bindings(resource, event, trigger, **kwargs): + """List nsx router bindings entries without real objects behind them""" + orphaned_list = get_orphaned_router_bindings() + LOG.info(formatters.output_formatter( + constants.ORPHANED_BINDINGS, orphaned_list, + ['edge_id', 'router_id', 'availability_zone', 'status'])) + + +def get_orphaned_router_bindings(): + context = n_context.get_admin_context() + orphaned_list = [] + + with utils.NsxVPluginWrapper() as plugin: + networks = plugin.get_networks(context, fields=['id']) + net_ids = [x['id'] for x in networks] + routers = plugin.get_routers(context, fields=['id']) + rtr_ids = [x['id'] for x in routers] + + for binding in get_router_edge_bindings(): + if not router_binding_obj_exist(context, binding, + net_ids, rtr_ids): + orphaned_list.append(binding) + return orphaned_list + + +def _get_obj_id_from_binding(router_id, prefix): + """Return the id part of the router-binding router-id field""" + return router_id[len(prefix):] + + +def _is_id_prefix_in_list(id_prefix, ids): + """Return True if the id_prefix is the prefix of one of the ids""" + for x in ids: + if x.startswith(id_prefix): + return True + return False + + +def router_binding_obj_exist(context, binding, net_ids, rtr_ids): + """Check if the object responsible for the router binding entry exists + + Check if the relevant router/network/loadbalancer exists in the neutron DB + """ + router_id = binding.router_id + + if router_id.startswith(vcns_const.BACKUP_ROUTER_PREFIX): + # no neutron object that should match backup edges + return True + + if router_id.startswith(vcns_const.DHCP_EDGE_PREFIX): + # should have a network starting with this id + # get the id. and look for a network with this id + net_id_prefix = _get_obj_id_from_binding( + router_id, vcns_const.DHCP_EDGE_PREFIX) + if _is_id_prefix_in_list(net_id_prefix, net_ids): + return True + else: + LOG.warning("Network for binding entry %s not found", router_id) + return False + + if router_id.startswith(vcns_const.PLR_EDGE_PREFIX): + # should have a distributed router starting with this id + # get the id. and look for a network with this id + rtr_id_prefix = _get_obj_id_from_binding( + router_id, vcns_const.PLR_EDGE_PREFIX) + + if _is_id_prefix_in_list(rtr_id_prefix, rtr_ids): + return True + else: + LOG.warning("Router for binding entry %s not found", router_id) + return False + + if router_id.startswith(lb_common.RESOURCE_ID_PFX): + # should have a load balancer starting with this id on the same edge + if nsxv_db.get_nsxv_lbaas_loadbalancer_binding_by_edge( + context.session, binding.edge_id): + return True + else: + LOG.warning("Loadbalancer for binding entry %s not found", + router_id) + return False + + # regular router + # get the id. and look for a router with this id + if _is_id_prefix_in_list(router_id, rtr_ids): + return True + else: + LOG.warning("Router for binding entry %s not found", router_id) + return False + + def get_orphaned_edges(): nsxv_edge_ids = set() for edge in utils.get_nsxv_backend_edges(): @@ -530,3 +655,9 @@ registry.subscribe(nsx_update_edge, registry.subscribe(nsx_update_edges, constants.EDGES, shell.Operations.NSX_UPDATE_ALL.value) +registry.subscribe(list_orphaned_router_bindings, + constants.ORPHANED_BINDINGS, + shell.Operations.LIST.value) +registry.subscribe(clean_orphaned_router_bindings, + constants.ORPHANED_BINDINGS, + shell.Operations.CLEAN.value) diff --git a/vmware_nsx/shell/resources.py b/vmware_nsx/shell/resources.py index c1cc5b985e..3722a3d9ea 100644 --- a/vmware_nsx/shell/resources.py +++ b/vmware_nsx/shell/resources.py @@ -145,6 +145,9 @@ nsxv_resources = { constants.ORPHANED_EDGES: Resource(constants.ORPHANED_EDGES, [Operations.LIST.value, Operations.CLEAN.value]), + constants.ORPHANED_BINDINGS: Resource(constants.ORPHANED_BINDINGS, + [Operations.LIST.value, + Operations.CLEAN.value]), constants.MISSING_EDGES: Resource(constants.MISSING_EDGES, [Operations.LIST.value]), constants.SPOOFGUARD_POLICY: Resource(constants.SPOOFGUARD_POLICY,