Merge pull request #8679 from louis-oui/bgp-summary-filter

bgpd: add show bgp summary filter by neighbor or AS
This commit is contained in:
Donatas Abraitis 2021-05-27 13:57:14 +03:00 committed by GitHub
commit b8fd5ba1d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 283 additions and 106 deletions

View File

@ -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] [<neighbor <A.B.C.D|X:X::X:X|WORD>|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)

View File

@ -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 [<view|vrf> 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
DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd,
"show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR
" [" BGP_SAFI_WITH_LABEL_CMD_STR
"]] [all$all] summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|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"
"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)
{
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)

View File

@ -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,

View File

@ -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 <internal|external|ASN> [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

View File

@ -904,23 +904,26 @@ 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()
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" 2> /dev/null')
.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)
#
# 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)
@ -944,19 +947,63 @@ def test_bgp_summary():
r"No IPv4 labeled-unicast neighbor is configured", "", actual
)
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()
actual = actual.rstrip()
#
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
# 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",
title2="expected SHOW IP BGP SUMMARY",
title1="actual SHOW IP BGP SUMMARY " + filter.upper() ,
title2="expected SHOW IP BGP SUMMARY " + filter.upper(),
)
# Empty string if it matches, otherwise diff contains unified diff
@ -973,6 +1020,13 @@ def test_bgp_summary():
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):
fatal_error = net["r%s" % i].checkRouterRunning()