Merge pull request #17431 from krishna-samy/bgpd_json_commits

bgpd: show json output changes to optimize various show commands
This commit is contained in:
Russ White 2025-01-07 08:43:55 -05:00 committed by GitHub
commit c9c9608c70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 288 additions and 123 deletions

View file

@ -3108,6 +3108,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
afi_t afi;
safi_t safi;
uint32_t prefix_cnt, path_cnt;
int first = true;
afi = AFI_L2VPN;
safi = SAFI_EVPN;
@ -3132,8 +3133,15 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
prefix_rd2str((struct prefix_rd *)rd_destp, rd_str,
sizeof(rd_str), bgp->asnotation);
if (json)
if (json) {
if (first) {
vty_out(vty, "\"%s\":", rd_str);
first = false;
} else {
vty_out(vty, ",\"%s\":", rd_str);
}
json_rd = json_object_new_object();
}
rd_header = 1;
@ -3247,18 +3255,18 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
}
if (json) {
if (add_rd_to_json)
json_object_object_add(json, rd_str, json_rd);
else {
if (add_rd_to_json) {
vty_json_no_pretty(vty, json_rd);
} else {
vty_out(vty, "{}");
json_object_free(json_rd);
json_rd = NULL;
}
}
}
if (json) {
json_object_int_add(json, "numPrefix", prefix_cnt);
json_object_int_add(json, "numPaths", path_cnt);
vty_out(vty, ",\"numPrefix\":%u", prefix_cnt);
vty_out(vty, ",\"numPaths\":%u", path_cnt);
} else {
if (prefix_cnt == 0) {
vty_out(vty, "No EVPN prefixes %sexist\n",
@ -3276,20 +3284,18 @@ int bgp_evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
{
json_object *json = NULL;
if (use_json)
if (use_json) {
json = json_object_new_object();
vty_out(vty, "{\n");
}
evpn_show_all_routes(vty, bgp, type, json, detail, false);
if (use_json)
/*
* We are using no_pretty here because under extremely high
* settings (lots of routes with many different paths) this can
* save several minutes of output when FRR is run on older cpu's
* or more underperforming routers out there. So for route
* scale, we need to use no_pretty json.
*/
vty_json_no_pretty(vty, json);
if (use_json) {
vty_out(vty, "}\n");
json_object_free(json);
}
return CMD_SUCCESS;
}
@ -4940,8 +4946,10 @@ DEFUN(show_bgp_l2vpn_evpn_route,
if (!bgp)
return CMD_WARNING;
if (uj)
if (uj) {
json = json_object_new_object();
vty_out(vty, "{\n");
}
if (bgp_evpn_cli_parse_type(&type, argv, argc) < 0)
return CMD_WARNING;
@ -4954,13 +4962,10 @@ DEFUN(show_bgp_l2vpn_evpn_route,
evpn_show_all_routes(vty, bgp, type, json, detail, self_orig);
/*
* This is an extremely expensive operation at scale
* and as such we need to save as much time as is
* possible.
*/
if (uj)
vty_json_no_pretty(vty, json);
if (uj) {
vty_out(vty, "}\n");
json_object_free(json);
}
return CMD_SUCCESS;
}
@ -5017,10 +5022,20 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd,
if (bgp_evpn_cli_parse_type(&type, argv, argc) < 0)
return CMD_WARNING;
if (rd_all)
if (rd_all) {
if (uj)
vty_out(vty, "{\n");
evpn_show_all_routes(vty, bgp, type, json, 1, false);
else
if (uj) {
vty_out(vty, "}\n");
json_object_free(json);
return CMD_SUCCESS;
}
} else {
evpn_show_route_rd(vty, bgp, &prd, type, json);
}
if (uj)
vty_json(vty, json);

View file

@ -970,9 +970,8 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp,
json_object_object_add(json, "nexthops", json_gates);
}
static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
struct bgp_nexthop_cache *bnc, bool specific,
json_object *json)
static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, struct bgp_nexthop_cache *bnc,
bool detail, bool uj)
{
char buf[PREFIX2STR_BUFFER];
time_t tbuf;
@ -983,10 +982,10 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
peer = (struct peer *)bnc->nht_info;
if (json)
if (uj)
json_nexthop = json_object_new_object();
if (bnc->srte_color) {
if (json)
if (uj)
json_object_int_add(json_nexthop, "srteColor",
bnc->srte_color);
else
@ -994,7 +993,7 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
}
inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, buf, sizeof(buf));
if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)) {
if (json) {
if (uj) {
json_object_boolean_true_add(json_nexthop, "valid");
json_object_boolean_true_add(json_nexthop, "complete");
json_object_int_add(json_nexthop, "igpMetric",
@ -1022,7 +1021,7 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
}
bgp_show_nexthops_detail(vty, bgp, bnc, json_nexthop);
} else if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE)) {
if (json) {
if (uj) {
json_object_boolean_true_add(json_nexthop, "valid");
json_object_boolean_false_add(json_nexthop, "complete");
json_object_int_add(json_nexthop, "igpMetric",
@ -1042,7 +1041,7 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
}
bgp_show_nexthops_detail(vty, bgp, bnc, json_nexthop);
} else {
if (json) {
if (uj) {
json_object_boolean_false_add(json_nexthop, "valid");
json_object_boolean_false_add(json_nexthop, "complete");
json_object_int_add(json_nexthop, "pathCount",
@ -1074,8 +1073,8 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
}
}
tbuf = time(NULL) - (monotime(NULL) - bnc->last_update);
if (json) {
if (!specific) {
if (uj) {
if (detail) {
json_last_update = json_object_new_object();
json_object_int_add(json_last_update, "epoch", tbuf);
json_object_string_add(json_last_update, "string",
@ -1090,22 +1089,25 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
}
/* show paths dependent on nexthop, if needed. */
if (specific)
if (detail)
bgp_show_nexthop_paths(vty, bgp, bnc, json_nexthop);
if (json)
json_object_object_add(json, buf, json_nexthop);
if (uj) {
vty_out(vty, "\"%s\":", buf);
vty_json_no_pretty(vty, json_nexthop);
}
}
static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp,
bool import_table, json_object *json, afi_t afi,
bool detail)
static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp, bool import_table, bool uj,
afi_t afi, bool detail)
{
struct bgp_nexthop_cache *bnc;
struct bgp_nexthop_cache_head(*tree)[AFI_MAX];
json_object *json_afi = NULL;
bool found = false;
bool firstafi = true;
bool firstnh = true;
if (!json) {
if (!uj) {
if (import_table)
vty_out(vty, "Current BGP import check cache:\n");
else
@ -1117,34 +1119,42 @@ static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp,
tree = &bgp->nexthop_cache_table;
if (afi == AFI_IP || afi == AFI_IP6) {
if (json)
json_afi = json_object_new_object();
if (uj)
vty_out(vty, "%s:{", (afi == AFI_IP) ? "\"ipv4\"" : "\"ipv6\"");
frr_each (bgp_nexthop_cache, &(*tree)[afi], bnc) {
bgp_show_nexthop(vty, bgp, bnc, detail, json_afi);
if (uj)
vty_out(vty, "%s", firstnh ? "" : ",");
bgp_show_nexthop(vty, bgp, bnc, detail, uj);
found = true;
firstnh = false;
}
if (found && json)
json_object_object_add(
json, (afi == AFI_IP) ? "ipv4" : "ipv6",
json_afi);
if (found && uj)
vty_out(vty, "}");
return;
}
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
if (json && (afi == AFI_IP || afi == AFI_IP6))
json_afi = json_object_new_object();
frr_each (bgp_nexthop_cache, &(*tree)[afi], bnc)
bgp_show_nexthop(vty, bgp, bnc, detail, json_afi);
if (json && (afi == AFI_IP || afi == AFI_IP6))
json_object_object_add(
json, (afi == AFI_IP) ? "ipv4" : "ipv6",
json_afi);
if (afi != AFI_IP && afi != AFI_IP6)
continue;
if (uj)
vty_out(vty, "%s%s:{", firstafi ? "" : ",",
(afi == AFI_IP) ? "\"ipv4\"" : "\"ipv6\"");
firstafi = false;
firstnh = true;
frr_each (bgp_nexthop_cache, &(*tree)[afi], bnc) {
if (uj)
vty_out(vty, "%s", firstnh ? "" : ",");
bgp_show_nexthop(vty, bgp, bnc, detail, uj);
firstnh = false;
}
if (uj)
vty_out(vty, "}");
}
}
static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name,
const char *nhopip_str, bool import_table,
json_object *json, afi_t afi, bool detail)
static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name, const char *nhopip_str,
bool import_table, bool uj, afi_t afi, bool detail)
{
struct bgp *bgp;
@ -1153,7 +1163,7 @@ static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name,
else
bgp = bgp_get_default();
if (!bgp) {
if (!json)
if (!uj)
vty_out(vty, "%% No such BGP instance exist\n");
return CMD_WARNING;
}
@ -1163,61 +1173,57 @@ static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name,
struct bgp_nexthop_cache_head (*tree)[AFI_MAX];
struct bgp_nexthop_cache *bnc;
bool found = false;
json_object *json_afi = NULL;
if (!str2prefix(nhopip_str, &nhop)) {
if (!json)
if (!uj)
vty_out(vty, "nexthop address is malformed\n");
return CMD_WARNING;
}
tree = import_table ? &bgp->import_check_table
: &bgp->nexthop_cache_table;
if (json)
json_afi = json_object_new_object();
if (uj)
vty_out(vty, "%s:{",
(family2afi(nhop.family) == AFI_IP) ? "\"ipv4\"" : "\"ipv6\"");
frr_each (bgp_nexthop_cache, &(*tree)[family2afi(nhop.family)],
bnc) {
if (prefix_cmp(&bnc->prefix, &nhop))
continue;
bgp_show_nexthop(vty, bgp, bnc, true, json_afi);
bgp_show_nexthop(vty, bgp, bnc, true, uj);
found = true;
}
if (json)
json_object_object_add(
json,
(family2afi(nhop.family) == AFI_IP) ? "ipv4"
: "ipv6",
json_afi);
if (!found && !json)
if (!found && !uj)
vty_out(vty, "nexthop %s does not have entry\n",
nhopip_str);
if (uj)
vty_out(vty, "}");
} else
bgp_show_nexthops(vty, bgp, import_table, json, afi, detail);
bgp_show_nexthops(vty, bgp, import_table, uj, afi, detail);
return CMD_SUCCESS;
}
static void bgp_show_all_instances_nexthops_vty(struct vty *vty,
json_object *json, afi_t afi,
bool detail)
static void bgp_show_all_instances_nexthops_vty(struct vty *vty, bool uj, afi_t afi, bool detail)
{
struct listnode *node, *nnode;
struct bgp *bgp;
const char *inst_name;
json_object *json_instance = NULL;
bool firstinst = true;
for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
inst_name = (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
? VRF_DEFAULT_NAME
: bgp->name;
if (json)
json_instance = json_object_new_object();
if (uj)
vty_out(vty, "%s\"%s\":{", firstinst ? "" : ",", inst_name);
else
vty_out(vty, "\nInstance %s:\n", inst_name);
bgp_show_nexthops(vty, bgp, false, json_instance, afi, detail);
if (json)
json_object_object_add(json, inst_name, json_instance);
bgp_show_nexthops(vty, bgp, false, uj, afi, detail);
firstinst = false;
if (uj)
vty_out(vty, "}");
}
}
@ -1241,20 +1247,18 @@ DEFPY (show_ip_bgp_nexthop,
JSON_STR)
{
int rc = 0;
json_object *json = NULL;
afi_t afiz = AFI_UNSPEC;
if (uj)
json = json_object_new_object();
vty_out(vty, "{\n");
if (afi)
afiz = bgp_vty_afi_from_str(afi);
rc = show_ip_bgp_nexthop_table(vty, vrf, nhop_str, false, json, afiz,
detail);
rc = show_ip_bgp_nexthop_table(vty, vrf, nhop_str, false, uj, afiz, detail);
if (uj)
vty_json(vty, json);
vty_out(vty, "}\n");
return rc;
}
@ -1271,16 +1275,14 @@ DEFPY (show_ip_bgp_import_check,
JSON_STR)
{
int rc = 0;
json_object *json = NULL;
if (uj)
json = json_object_new_object();
vty_out(vty, "{\n");
rc = show_ip_bgp_nexthop_table(vty, vrf, NULL, true, json, AFI_UNSPEC,
detail);
rc = show_ip_bgp_nexthop_table(vty, vrf, NULL, true, uj, AFI_UNSPEC, detail);
if (uj)
vty_json(vty, json);
vty_out(vty, "}\n");
return rc;
}
@ -1298,19 +1300,18 @@ DEFPY (show_ip_bgp_instance_all_nexthop,
"Show detailed information\n"
JSON_STR)
{
json_object *json = NULL;
afi_t afiz = AFI_UNSPEC;
if (uj)
json = json_object_new_object();
vty_out(vty, "{");
if (afi)
afiz = bgp_vty_afi_from_str(afi);
bgp_show_all_instances_nexthops_vty(vty, json, afiz, detail);
bgp_show_all_instances_nexthops_vty(vty, uj, afiz, detail);
if (uj)
vty_json(vty, json);
vty_out(vty, "}");
return CMD_SUCCESS;
}

View file

@ -15106,6 +15106,8 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
json_object *json = NULL;
json_object *json_ar = NULL;
bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
bool first = true;
struct update_subgroup *subgrp;
/* Init BGP headers here so they're only displayed once
* even if 'table' is 2-tier (MPLS_VPN, ENCAP, EVPN).
@ -15174,6 +15176,28 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
else
table = bgp->rib[afi][safi];
subgrp = peer_subgroup(peer, afi, safi);
if (use_json) {
if (type == bgp_show_adj_route_advertised || type == bgp_show_adj_route_received) {
if (header1) {
int version = table ? table->version : 0;
vty_out(vty, "\"bgpTableVersion\":%d", version);
vty_out(vty, ",\"bgpLocalRouterId\":\"%pI4\"", &bgp->router_id);
vty_out(vty, ",\"defaultLocPrf\":%u", bgp->default_local_pref);
vty_out(vty, ",\"localAS\":%u", bgp->as);
if (type == bgp_show_adj_route_advertised && subgrp &&
CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE))
vty_out(vty, ",\"bgpOriginatingDefaultNetwork\":\"%s\"",
(afi == AFI_IP) ? "0.0.0.0/0" : "::/0");
}
if (type == bgp_show_adj_route_advertised)
vty_out(vty, ",\"advertisedRoutes\": ");
if (type == bgp_show_adj_route_received)
vty_out(vty, ",\"receivedRoutes\": ");
}
}
if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)
|| (safi == SAFI_EVPN)) {
@ -15192,6 +15216,7 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
json_routes = json_object_new_object();
const struct prefix_rd *prd;
prd = (const struct prefix_rd *)bgp_dest_get_prefix(
dest);
@ -15205,34 +15230,56 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
&filtered_count_per_rd);
/* Don't include an empty RD in the output! */
if (json_routes && (output_count_per_rd > 0))
json_object_object_add(json_ar, rd_str,
json_routes);
if (json_routes && (output_count_per_rd > 0) && use_json) {
if (type == bgp_show_adj_route_advertised ||
type == bgp_show_adj_route_received) {
if (first) {
vty_out(vty, "\"%s\":", rd_str);
first = false;
} else {
vty_out(vty, ",\"%s\":", rd_str);
}
vty_json_no_pretty(vty, json_routes);
} else {
json_object_object_add(json_ar, rd_str, json_routes);
}
}
output_count += output_count_per_rd;
filtered_count += filtered_count_per_rd;
}
} else
} else {
show_adj_route(vty, peer, table, afi, safi, type, rmap_name,
json, json_ar, show_flags, &header1, &header2,
rd_str, match, &output_count, &filtered_count);
if (use_json) {
if (type == bgp_show_adj_route_advertised)
json_object_object_add(json, "advertisedRoutes",
json_ar);
else
json_object_object_add(json, "receivedRoutes", json_ar);
json_object_int_add(json, "totalPrefixCounter", output_count);
json_object_int_add(json, "filteredPrefixCounter",
filtered_count);
if (use_json) {
if (type == bgp_show_adj_route_advertised ||
type == bgp_show_adj_route_received) {
vty_json_no_pretty(vty, json_ar);
}
}
}
/*
* This is an extremely expensive operation at scale
* and non-pretty reduces memory footprint significantly.
*/
vty_json_no_pretty(vty, json);
} else if (output_count > 0) {
if (use_json) {
if (type == bgp_show_adj_route_advertised || type == bgp_show_adj_route_received) {
vty_out(vty, ",\"totalPrefixCounter\":%lu", output_count);
vty_out(vty, ",\"filteredPrefixCounter\":%lu", filtered_count);
json_object_free(json);
} else {
/* for bgp_show_adj_route_filtered & bgp_show_adj_route_bestpath type */
json_object_object_add(json, "receivedRoutes", json_ar);
json_object_int_add(json, "totalPrefixCounter", output_count);
json_object_int_add(json, "filteredPrefixCounter", filtered_count);
}
/*
* This is an extremely expensive operation at scale
* and non-pretty reduces memory footprint significantly.
*/
if ((type != bgp_show_adj_route_advertised) && (type != bgp_show_adj_route_received))
vty_json_no_pretty(vty, json);
} else if (output_count > 0) {
if (!match && filtered_count > 0)
vty_out(vty,
"\nTotal number of prefixes %ld (%ld filtered)\n",
@ -15335,6 +15382,7 @@ DEFPY(show_ip_bgp_instance_neighbor_advertised_route,
uint16_t show_flags = 0;
struct listnode *node;
struct bgp *abgp;
int ret;
if (detail || prefix_str)
SET_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL);
@ -15376,9 +15424,22 @@ DEFPY(show_ip_bgp_instance_neighbor_advertised_route,
else if (argv_find(argv, argc, "filtered-routes", &idx))
type = bgp_show_adj_route_filtered;
if (!all)
return peer_adj_routes(vty, peer, afi, safi, type, route_map,
prefix_str ? prefix : NULL, show_flags);
if (!all) {
if (uj)
if (type == bgp_show_adj_route_advertised ||
type == bgp_show_adj_route_received)
vty_out(vty, "{\n");
ret = peer_adj_routes(vty, peer, afi, safi, type, route_map,
prefix_str ? prefix : NULL, show_flags);
if (uj)
if (type == bgp_show_adj_route_advertised ||
type == bgp_show_adj_route_received)
vty_out(vty, "}\n");
return ret;
}
if (uj)
vty_out(vty, "{\n");

View file

@ -11476,6 +11476,72 @@ DEFPY (show_bgp_vrfs,
return CMD_SUCCESS;
}
DEFPY(show_bgp_router,
show_bgp_router_cmd,
"show bgp router [json]",
SHOW_STR
BGP_STR
"Overall BGP information\n"
JSON_STR)
{
char timebuf[MONOTIME_STRLEN];
time_t unix_timestamp;
bool uj = use_json(argc, argv);
json_object *json = NULL;
if (uj)
json = json_object_new_object();
time_to_string(bm->start_time, timebuf);
if (uj) {
unix_timestamp = time(NULL) - (monotime(NULL) - bm->start_time);
json_object_int_add(json, "bgpStartedAt", unix_timestamp);
json_object_boolean_add(json, "bgpStartedGracefully",
CHECK_FLAG(bm->flags, BM_FLAG_GRACEFUL_RESTART));
}
if (CHECK_FLAG(bm->flags, BM_FLAG_GRACEFUL_RESTART)) {
if (!uj)
vty_out(vty, "BGP started gracefully at %s", timebuf);
else
json_object_boolean_add(json, "grComplete",
CHECK_FLAG(bm->flags, BM_FLAG_GR_COMPLETE));
if (CHECK_FLAG(bm->flags, BM_FLAG_GR_COMPLETE)) {
time_to_string(bm->gr_completion_time, timebuf);
if (uj) {
unix_timestamp = time(NULL) -
(monotime(NULL) - bm->gr_completion_time);
json_object_int_add(json, "grCompletedAt", unix_timestamp);
} else
vty_out(vty, "Graceful restart completed at %s", timebuf);
} else {
if (!uj)
vty_out(vty, "Graceful restart is in progress\n");
}
} else {
if (!uj)
vty_out(vty, "BGP started at %s", timebuf);
}
if (uj) {
json_object_boolean_add(json, "bgpInMaintenanceMode",
(CHECK_FLAG(bm->flags, BM_FLAG_MAINTENANCE_MODE)));
json_object_int_add(json, "bgpInstanceCount", listcount(bm->bgp));
vty_json(vty, json);
} else {
if (CHECK_FLAG(bm->flags, BM_FLAG_MAINTENANCE_MODE))
vty_out(vty, "BGP is in Maintenance mode (BGP GSHUT is in effect)\n");
vty_out(vty, "Number of BGP instances (including default): %d\n",
listcount(bm->bgp));
}
return CMD_SUCCESS;
}
DEFUN (show_bgp_mac_hash,
show_bgp_mac_hash_cmd,
"show bgp mac hash",
@ -21927,6 +21993,9 @@ void bgp_vty_init(void)
/* "show [ip] bgp vrfs" commands. */
install_element(VIEW_NODE, &show_bgp_vrfs_cmd);
/* Some overall BGP information */
install_element(VIEW_NODE, &show_bgp_router_cmd);
/* Community-list. */
community_list_vty();

View file

@ -4350,6 +4350,10 @@ displays IPv6 routing table.
If ``detail`` option is specified after ``json``, more verbose JSON output
will be displayed.
.. clicmd:: show bgp router [json]
This command displays information related BGP router and Graceful Restart.
Some other commands provide additional options for filtering the output.
.. clicmd:: show [ip] bgp regexp LINE

View file

@ -187,6 +187,16 @@ def test_bgp_administrative_reset_gr():
"""
)
def _bgp_verify_show_bgp_router_json():
output = json.loads(r1.vtysh_cmd("show bgp router json"))
expected = {
"bgpStartedAt": "*",
"bgpStartedGracefully": False,
"bgpInMaintenanceMode": False,
"bgpInstanceCount": 1,
}
return topotest.json_cmp(output, expected)
step("Initial BGP converge")
test_func = functools.partial(_bgp_converge)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
@ -205,6 +215,11 @@ def test_bgp_administrative_reset_gr():
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Failed to send Administrative Reset notification from R2"
step("Check show bgp router json")
test_func = functools.partial(_bgp_verify_show_bgp_router_json)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Invalid BGP router details"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]