From: Louis Scalbert Date: Wed, 12 May 2021 15:17:56 +0000 (+0200) Subject: bgpd: add show bgp summary filter by neighbor or AS X-Git-Tag: base_8.1~488^2 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=8c1d4cd512af258dfab9379117157f85dcf102e8;p=matthieu%2Ffrr.git bgpd: add show bgp summary filter by neighbor or AS Add ability to filter session on show bgp summary by neighbor or remote AS: ubuntu# show bgp summary ? neighbor Show only the specified neighbor session remote-as Show only the specified remote AS session ubuntu# show bgp summary neighbor ? A.B.C.D Neighbor to display information about WORD Neighbor on BGP configured interface X:X::X:X Neighbor to display information about ubuntu# show bgp summary remote-as ? (1-4294967295) AS number external External (eBGP) AS sessions internal Internal (iBGP) AS sessions This patch includes the documentation and the topotest. Signed-off-by: Louis Scalbert --- diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index ed8a6a9506..fb3ba2c0ec 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -4309,24 +4309,30 @@ DEFPY(show_bgp_l2vpn_evpn_nh, /* * Display EVPN neighbor summary. */ -DEFUN(show_bgp_l2vpn_evpn_summary, - show_bgp_l2vpn_evpn_summary_cmd, - "show bgp [vrf VRFNAME] l2vpn evpn summary [established|failed] [wide] [json]", - SHOW_STR - BGP_STR +DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd, + "show bgp [vrf VRFNAME] l2vpn evpn summary [established|failed] [|remote-as <(1-4294967295)|internal|external>>] [wide] [json]", + SHOW_STR BGP_STR "bgp vrf\n" - "vrf name\n" - L2VPN_HELP_STR - EVPN_HELP_STR + "vrf name\n" L2VPN_HELP_STR EVPN_HELP_STR "Summary of BGP neighbor status\n" "Show only sessions in Established state\n" "Show only sessions not in Established state\n" - "Increase table width for longer output\n" - JSON_STR) + "Show only the specified neighbor session\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" + "Show only the specified remote AS sessions\n" + "AS number\n" + "Internal (iBGP) AS sessions\n" + "External (eBGP) AS sessions\n" + "Increase table width for longer output\n" JSON_STR) { int idx_vrf = 0; int idx = 0; char *vrf = NULL; + char *neighbor = NULL; + as_t as = 0; /* 0 means AS filter not set */ + int as_type = AS_UNSPECIFIED; uint8_t show_flags = 0; if (argv_find(argv, argc, "vrf", &idx_vrf)) @@ -4338,13 +4344,27 @@ DEFUN(show_bgp_l2vpn_evpn_summary, if (argv_find(argv, argc, "established", &idx)) SET_FLAG(show_flags, BGP_SHOW_OPT_ESTABLISHED); + + if (argv_find(argv, argc, "neighbor", &idx)) + neighbor = argv[idx + 1]->arg; + + if (argv_find(argv, argc, "remote-as", &idx)) { + if (argv[idx + 1]->arg[0] == 'i') + as_type = AS_INTERNAL; + else if (argv[idx + 1]->arg[0] == 'e') + as_type = AS_EXTERNAL; + else + as = (as_t)atoi(argv[idx + 1]->arg); + } + if (argv_find(argv, argc, "wide", &idx)) SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE); if (use_json(argc, argv)) SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); - return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, show_flags); + return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, neighbor, + as_type, as, show_flags); } int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv, int argc) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 77f1aadff7..b2769e21d9 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -10685,8 +10685,35 @@ static char *bgp_peer_description_stripped(char *desc, uint32_t size) return stripped; } +/* Determine whether var peer should be filtered out of the summary. */ +static bool bgp_show_summary_is_peer_filtered(struct peer *peer, + struct peer *fpeer, int as_type, + as_t as) +{ + + /* filter neighbor XXXX */ + if (fpeer && fpeer != peer) + return true; + + /* filter remote-as (internal|external) */ + if (as_type != AS_UNSPECIFIED) { + if (peer->as_type == AS_SPECIFIED) { + if (as_type == AS_INTERNAL) { + if (peer->as != peer->local_as) + return true; + } else if (peer->as == peer->local_as) + return true; + } else if (as_type != peer->as_type) + return true; + } else if (as && as != peer->as) /* filter remote-as XXX */ + return true; + + return false; +} + /* Show BGP peer's summary information. */ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, + struct peer *fpeer, int as_type, as_t as, uint8_t show_flags) { struct peer *peer; @@ -10723,6 +10750,12 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json = json_object_new_object(); json_peers = json_object_new_object(); for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (bgp_show_summary_is_peer_filtered(peer, fpeer, + as_type, as)) { + count++; + continue; + } + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) continue; @@ -10742,6 +10775,12 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, * characters are needed for the Neighbor column */ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (bgp_show_summary_is_peer_filtered(peer, fpeer, + as_type, as)) { + count++; + continue; + } + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) continue; @@ -11025,6 +11064,10 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, if (use_json) { json_peer = NULL; + if (bgp_show_summary_is_peer_filtered(peer, fpeer, + as_type, as)) + continue; + if (show_failed && bgp_has_peer_failed(peer, afi, safi)) { json_peer = json_object_new_object(); @@ -11174,6 +11217,9 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_object_add(json_peers, peer->host, json_peer); } else { + if (bgp_show_summary_is_peer_filtered(peer, fpeer, + as_type, as)) + continue; if (show_failed && bgp_has_peer_failed(peer, afi, safi)) { bgp_show_failed_summary(vty, bgp, peer, NULL, @@ -11183,7 +11229,6 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, if (show_established && bgp_has_peer_failed(peer, afi, safi)) continue; - memset(dn_flag, '\0', sizeof(dn_flag)); if (peer_dynamic_neighbor(peer)) { dn_flag[0] = '*'; @@ -11336,7 +11381,8 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, } static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, - int safi, uint8_t show_flags) + int safi, struct peer *fpeer, int as_type, + as_t as, uint8_t show_flags) { int is_first = 1; int afi_wildcard = (afi == AFI_MAX); @@ -11380,8 +11426,8 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, false)); } } - bgp_show_summary(vty, bgp, afi, safi, - show_flags); + bgp_show_summary(vty, bgp, afi, safi, fpeer, + as_type, as, show_flags); } safi++; if (!safi_wildcard) @@ -11403,10 +11449,14 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, } static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, - safi_t safi, uint8_t show_flags) + safi_t safi, + const char *neighbor, + int as_type, as_t as, + uint8_t show_flags) { struct listnode *node, *nnode; struct bgp *bgp; + struct peer *fpeer = NULL; int is_first = 1; bool nbr_output = false; bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON); @@ -11432,7 +11482,14 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, ? VRF_DEFAULT_NAME : bgp->name); } - bgp_show_summary_afi_safi(vty, bgp, afi, safi, show_flags); + if (neighbor) { + fpeer = peer_lookup_in_view(vty, bgp, neighbor, + use_json); + if (!fpeer) + continue; + } + bgp_show_summary_afi_safi(vty, bgp, afi, safi, fpeer, as_type, + as, show_flags); } if (use_json) @@ -11442,15 +11499,18 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, } int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, - safi_t safi, uint8_t show_flags) + safi_t safi, const char *neighbor, int as_type, + as_t as, uint8_t show_flags) { struct bgp *bgp; bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON); + struct peer *fpeer = NULL; if (name) { if (strmatch(name, "all")) { bgp_show_all_instances_summary_vty(vty, afi, safi, - show_flags); + neighbor, as_type, + as, show_flags); return CMD_SUCCESS; } else { bgp = bgp_lookup_by_name(name); @@ -11464,17 +11524,30 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, return CMD_WARNING; } - bgp_show_summary_afi_safi(vty, bgp, afi, safi, - show_flags); + if (neighbor) { + fpeer = peer_lookup_in_view(vty, bgp, neighbor, + use_json); + if (!fpeer) + return CMD_WARNING; + } + bgp_show_summary_afi_safi(vty, bgp, afi, safi, fpeer, + as_type, as, show_flags); return CMD_SUCCESS; } } bgp = bgp_get_default(); - if (bgp) - bgp_show_summary_afi_safi(vty, bgp, afi, safi, show_flags); - else { + if (bgp) { + if (neighbor) { + fpeer = peer_lookup_in_view(vty, bgp, neighbor, + use_json); + if (!fpeer) + return CMD_WARNING; + } + bgp_show_summary_afi_safi(vty, bgp, afi, safi, fpeer, as_type, + as, show_flags); + } else { if (use_json) vty_out(vty, "{}\n"); else @@ -11486,25 +11559,31 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, } /* `show [ip] bgp summary' commands. */ -DEFPY (show_ip_bgp_summary, - show_ip_bgp_summary_cmd, - "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [all$all] summary [established|failed] [wide] [json$uj]", - SHOW_STR - IP_STR - BGP_STR - BGP_INSTANCE_HELP_STR - BGP_AFI_HELP_STR - BGP_SAFI_WITH_LABEL_HELP_STR - "Display the entries for all address families\n" - "Summary of BGP neighbor status\n" - "Show only sessions in Established state\n" - "Show only sessions not in Established state\n" - "Increase table width for longer output\n" - JSON_STR) +DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd, + "show [ip] bgp [ VIEWVRFNAME] [" BGP_AFI_CMD_STR + " [" BGP_SAFI_WITH_LABEL_CMD_STR + "]] [all$all] summary [established|failed] [|remote-as <(1-4294967295)|internal|external>>] [wide] [json$uj]", + SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR + BGP_SAFI_WITH_LABEL_HELP_STR + "Display the entries for all address families\n" + "Summary of BGP neighbor status\n" + "Show only sessions in Established state\n" + "Show only sessions not in Established state\n" + "Show only the specified neighbor session\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" + "Show only the specified remote AS sessions\n" + "AS number\n" + "Internal (iBGP) AS sessions\n" + "External (eBGP) AS sessions\n" + "Increase table width for longer output\n" JSON_STR) { char *vrf = NULL; afi_t afi = AFI_MAX; safi_t safi = SAFI_MAX; + as_t as = 0; /* 0 means AS filter not set */ + int as_type = AS_UNSPECIFIED; uint8_t show_flags = 0; int idx = 0; @@ -11531,13 +11610,23 @@ DEFPY (show_ip_bgp_summary, if (argv_find(argv, argc, "established", &idx)) SET_FLAG(show_flags, BGP_SHOW_OPT_ESTABLISHED); + if (argv_find(argv, argc, "remote-as", &idx)) { + if (argv[idx + 1]->arg[0] == 'i') + as_type = AS_INTERNAL; + else if (argv[idx + 1]->arg[0] == 'e') + as_type = AS_EXTERNAL; + else + as = (as_t)atoi(argv[idx + 1]->arg); + } + if (argv_find(argv, argc, "wide", &idx)) SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE); if (argv_find(argv, argc, "json", &idx)) SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); - return bgp_show_summary_vty(vty, vrf, afi, safi, show_flags); + return bgp_show_summary_vty(vty, vrf, afi, safi, neighbor, as_type, as, + show_flags); } const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json) diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 051b2e9580..2531488d0d 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -185,7 +185,8 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty, int bgp_vty_find_and_parse_bgp(struct vty *vty, struct cmd_token **argv, int argc, struct bgp **bgp, bool use_json); extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, - safi_t safi, uint8_t show_flags); + safi_t safi, const char *neighbor, int as_type, + as_t as, uint8_t show_flags); extern int bgp_clear_star_soft_in(const char *name, char *errmsg, size_t errmsg_len); extern int bgp_clear_star_soft_out(const char *name, char *errmsg, diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 7f653cb7b7..dd042e2584 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -3284,6 +3284,19 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. Show a bgp peer summary for peers that are succesfully exchanging routes for the specified address family, and subsequent address-family. +.. clicmd:: show bgp [afi] [safi] [all] summary neighbor [PEER] [json] + + Show a bgp summary for the specified peer, address family, and + subsequent address-family. The neighbor filter can be used in combination + with the failed, established filters. + +.. clicmd:: show bgp [afi] [safi] [all] summary remote-as [json] + + Show a bgp peer summary for the specified remote-as ASN or type (``internal`` + for iBGP and ``external`` for eBGP sessions), address family, and subsequent + address-family. The remote-as filter can be used in combination with the + failed, established filters. + .. clicmd:: show bgp [afi] [safi] [neighbor [PEER] [routes|advertised-routes|received-routes] [json] This command shows information on a specific BGP peer of the relevant diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py index c10e32ad0a..597e230696 100644 --- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -904,74 +904,128 @@ def test_bgp_summary(): refTableFile = "%s/r%s/show_ip_bgp_summary.ref" % (thisDir, i) if os.path.isfile(refTableFile): # Read expected result from file - expected = open(refTableFile).read().rstrip() - # Fix newlines (make them all the same) - expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + expected_original = open(refTableFile).read().rstrip() - # Actual output from router - actual = ( - net["r%s" % i] - .cmd('vtysh -c "show ip bgp summary" 2> /dev/null') - .rstrip() - ) - # Mask out "using XXiXX bytes" portion. They are random... - actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual) - # Mask out "using XiXXX KiB" portion. They are random... - actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual) - # - # Remove extra summaries which exist with newer versions - # - # Remove summary lines (changed recently) - actual = re.sub(r"Total number.*", "", actual) - actual = re.sub(r"Displayed.*", "", actual) - # Remove IPv4 Unicast Summary (Title only) - actual = re.sub(r"IPv4 Unicast Summary:", "", actual) - # Remove IPv4 Multicast Summary (all of it) - actual = re.sub(r"IPv4 Multicast Summary:", "", actual) - actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual) - # Remove IPv4 VPN Summary (all of it) - actual = re.sub(r"IPv4 VPN Summary:", "", actual) - actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual) - # Remove IPv4 Encap Summary (all of it) - actual = re.sub(r"IPv4 Encap Summary:", "", actual) - actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual) - # Remove Unknown Summary (all of it) - actual = re.sub(r"Unknown Summary:", "", actual) - actual = re.sub(r"No Unknown neighbor is configured", "", actual) + for filter in ["", "remote-as internal", "remote-as external", + "remote-as 100", "remote-as 123", + "neighbor 192.168.7.10", "neighbor 192.168.7.10", + "neighbor fc00:0:0:8::1000", + "neighbor 10.0.0.1"]: + # Actual output from router + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ip bgp summary ' + filter + '" 2> /dev/null') + .rstrip() + ) + # Mask out "using XXiXX bytes" portion. They are random... + actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual) + # Mask out "using XiXXX KiB" portion. They are random... + actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual) - actual = re.sub(r"IPv4 labeled-unicast Summary:", "", actual) - actual = re.sub( - r"No IPv4 labeled-unicast neighbor is configured", "", actual - ) + # Remove extra summaries which exist with newer versions - # Strip empty lines - actual = actual.lstrip() - actual = actual.rstrip() - # - # Fix newlines (make them all the same) - actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + # Remove summary lines (changed recently) + actual = re.sub(r"Total number.*", "", actual) + actual = re.sub(r"Displayed.*", "", actual) + # Remove IPv4 Unicast Summary (Title only) + actual = re.sub(r"IPv4 Unicast Summary:", "", actual) + # Remove IPv4 Multicast Summary (all of it) + actual = re.sub(r"IPv4 Multicast Summary:", "", actual) + actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual) + # Remove IPv4 VPN Summary (all of it) + actual = re.sub(r"IPv4 VPN Summary:", "", actual) + actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual) + # Remove IPv4 Encap Summary (all of it) + actual = re.sub(r"IPv4 Encap Summary:", "", actual) + actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual) + # Remove Unknown Summary (all of it) + actual = re.sub(r"Unknown Summary:", "", actual) + actual = re.sub(r"No Unknown neighbor is configured", "", actual) + + actual = re.sub(r"IPv4 labeled-unicast Summary:", "", actual) + actual = re.sub( + r"No IPv4 labeled-unicast neighbor is configured", "", actual + ) - # Generate Diff - diff = topotest.get_textdiff( - actual, - expected, - title1="actual SHOW IP BGP SUMMARY", - title2="expected SHOW IP BGP SUMMARY", - ) + expected = expected_original + # apply filters on expected output + if "internal" in filter or "remote-as 100" in filter: + expected = re.sub(r".+\s+200\s+.+", "", expected) + elif "external" in filter: + expected = re.sub(r".+\s+100\s+.+Active.+", "", expected) + elif "remote-as 123" in filter: + expected = re.sub( + r"(192.168.7.(1|2)0|fc00:0:0:8::(1|2)000).+Active.+", + "", expected + ) + elif "192.168.7.10" in filter: + expected = re.sub( + r"(192.168.7.20|fc00:0:0:8::(1|2)000).+Active.+", + "", expected + ) + elif "fc00:0:0:8::1000" in filter: + expected = re.sub( + r"(192.168.7.(1|2)0|fc00:0:0:8::2000).+Active.+", + "", expected + ) + elif "10.0.0.1" in filter: + expected = "No such neighbor in this view/vrf" + + # Strip empty lines + actual = actual.lstrip().rstrip() + expected = expected.lstrip().rstrip() + actual = re.sub(r"\n+", "\n", actual) + expected = re.sub(r"\n+", "\n", expected) + + # reapply initial formatting + actual = re.sub(r"KiB of memory\n", "KiB of memory\n\n", actual) + expected = re.sub(r"KiB of memory\n", "KiB of memory\n\n", expected) + + # realign expected neighbor columns if needed + try: + idx_actual = re.search(r"\n(Neighbor\s+V\s+)", actual).group(1).find("V") + idx_expected = re.search(r"\n(Neighbor\s+V\s+)", expected).group(1).find("V") + idx_diff = idx_expected - idx_actual + if idx_diff > 0: + # Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd + expected = re.sub(" " * idx_diff + "V ", "V ", expected) + # 192.168.7.10 4 100 0 0 0 0 0 never Active + expected = re.sub(" " * idx_diff + "4 ", "4 ", expected) + except AttributeError: + pass - # Empty string if it matches, otherwise diff contains unified diff - if diff: - sys.stderr.write( - "r%s failed SHOW IP BGP SUMMARY check:\n%s\n" % (i, diff) + # Fix newlines (make them all the same) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual SHOW IP BGP SUMMARY " + filter.upper() , + title2="expected SHOW IP BGP SUMMARY " + filter.upper(), ) - failures += 1 - else: - print("r%s ok" % i) - assert failures == 0, "SHOW IP BGP SUMMARY failed for router r%s:\n%s" % ( - i, - diff, - ) + # Empty string if it matches, otherwise diff contains unified diff + if diff: + sys.stderr.write( + "r%s failed SHOW IP BGP SUMMARY check:\n%s\n" % (i, diff) + ) + failures += 1 + else: + print("r%s ok" % i) + + assert failures == 0, "SHOW IP BGP SUMMARY failed for router r%s:\n%s" % ( + i, + diff, + ) + + # Actual output from router + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ip bgp summary" 2> /dev/null') + .rstrip() + ) # Make sure that all daemons are running for i in range(1, 2):