forked from Mirror/frr
Merge pull request #16093 from louis-6wind/fix-show-route-memory
zebra: fix Out Of Memory issue when displaying large route tables in JSON
This commit is contained in:
commit
19c3e0eca6
15
lib/vty.c
15
lib/vty.c
|
@ -390,6 +390,21 @@ int vty_json_no_pretty(struct vty *vty, struct json_object *json)
|
||||||
return vty_json_helper(vty, json, JSON_C_TO_STRING_NOSLASHESCAPE);
|
return vty_json_helper(vty, json, JSON_C_TO_STRING_NOSLASHESCAPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void vty_json_key(struct vty *vty, const char *key, bool *first_key)
|
||||||
|
{
|
||||||
|
vty_out(vty, "%s\"%s\":", *first_key ? "{" : ",", key);
|
||||||
|
*first_key = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vty_json_close(struct vty *vty, bool first_key)
|
||||||
|
{
|
||||||
|
if (first_key)
|
||||||
|
/* JSON was not opened */
|
||||||
|
vty_out(vty, "{");
|
||||||
|
vty_out(vty, "}\n");
|
||||||
|
}
|
||||||
|
|
||||||
void vty_json_empty(struct vty *vty, struct json_object *json)
|
void vty_json_empty(struct vty *vty, struct json_object *json)
|
||||||
{
|
{
|
||||||
json_object *jsonobj = json;
|
json_object *jsonobj = json;
|
||||||
|
|
|
@ -378,6 +378,8 @@ extern bool vty_set_include(struct vty *vty, const char *regexp);
|
||||||
*/
|
*/
|
||||||
extern int vty_json(struct vty *vty, struct json_object *json);
|
extern int vty_json(struct vty *vty, struct json_object *json);
|
||||||
extern int vty_json_no_pretty(struct vty *vty, struct json_object *json);
|
extern int vty_json_no_pretty(struct vty *vty, struct json_object *json);
|
||||||
|
void vty_json_key(struct vty *vty, const char *key, bool *first_key);
|
||||||
|
void vty_json_close(struct vty *vty, bool first_key);
|
||||||
extern void vty_json_empty(struct vty *vty, struct json_object *json);
|
extern void vty_json_empty(struct vty *vty, struct json_object *json);
|
||||||
/* post fd to be passed to the vtysh client
|
/* post fd to be passed to the vtysh client
|
||||||
* fd is owned by the VTY code after this and will be closed when done
|
* fd is owned by the VTY code after this and will be closed when done
|
||||||
|
|
|
@ -338,6 +338,21 @@ interface EVA
|
||||||
result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
|
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Check that "show ip route vrf DONNA json" and the JSON at key "DONNA" of
|
||||||
|
"show ip route vrf all json" gives the same result.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def check_vrf_table(router, vrf, expect):
|
||||||
|
output = router.vtysh_cmd("show ip route vrf all json", isjson=True)
|
||||||
|
vrf_table = output.get(vrf, {})
|
||||||
|
|
||||||
|
return topotest.json_cmp(vrf_table, expect)
|
||||||
|
|
||||||
|
test_func = partial(check_vrf_table, r1, "DONNA", expect)
|
||||||
|
result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
|
||||||
|
|
||||||
|
|
||||||
def test_vrf_route_leak_donna_after_eva_up():
|
def test_vrf_route_leak_donna_after_eva_up():
|
||||||
logger.info("Ensure that route states change after EVA interface goes up")
|
logger.info("Ensure that route states change after EVA interface goes up")
|
||||||
|
|
|
@ -60,8 +60,8 @@ struct route_show_ctx {
|
||||||
};
|
};
|
||||||
|
|
||||||
static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
|
static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
|
||||||
safi_t safi, bool use_fib, json_object *vrf_json,
|
safi_t safi, bool use_fib, bool use_json,
|
||||||
bool use_json, route_tag_t tag,
|
route_tag_t tag,
|
||||||
const struct prefix *longer_prefix_p,
|
const struct prefix *longer_prefix_p,
|
||||||
bool supernets_only, int type,
|
bool supernets_only, int type,
|
||||||
unsigned short ospf_instance_id, uint32_t tableid,
|
unsigned short ospf_instance_id, uint32_t tableid,
|
||||||
|
@ -153,8 +153,8 @@ DEFPY (show_ip_rpf,
|
||||||
};
|
};
|
||||||
|
|
||||||
return do_show_ip_route(vty, VRF_DEFAULT_NAME, ip ? AFI_IP : AFI_IP6,
|
return do_show_ip_route(vty, VRF_DEFAULT_NAME, ip ? AFI_IP : AFI_IP6,
|
||||||
SAFI_MULTICAST, false, NULL, uj, 0, NULL, false,
|
SAFI_MULTICAST, false, uj, 0, NULL, false, 0, 0,
|
||||||
0, 0, 0, false, &ctx);
|
0, false, &ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFPY (show_ip_rpf_addr,
|
DEFPY (show_ip_rpf_addr,
|
||||||
|
@ -858,19 +858,20 @@ static void vty_show_ip_route_detail_json(struct vty *vty,
|
||||||
vty_json(vty, json);
|
vty_json(vty, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
|
||||||
do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
|
struct route_table *table, afi_t afi,
|
||||||
struct route_table *table, afi_t afi, bool use_fib,
|
bool use_fib, route_tag_t tag,
|
||||||
json_object *vrf_json, route_tag_t tag,
|
const struct prefix *longer_prefix_p,
|
||||||
const struct prefix *longer_prefix_p, bool supernets_only,
|
bool supernets_only, int type,
|
||||||
int type, unsigned short ospf_instance_id, bool use_json,
|
unsigned short ospf_instance_id, bool use_json,
|
||||||
uint32_t tableid, bool show_ng, struct route_show_ctx *ctx)
|
uint32_t tableid, bool show_ng,
|
||||||
|
struct route_show_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct route_node *rn;
|
struct route_node *rn;
|
||||||
struct route_entry *re;
|
struct route_entry *re;
|
||||||
|
bool first_json = true;
|
||||||
int first = 1;
|
int first = 1;
|
||||||
rib_dest_t *dest;
|
rib_dest_t *dest;
|
||||||
json_object *json = NULL;
|
|
||||||
json_object *json_prefix = NULL;
|
json_object *json_prefix = NULL;
|
||||||
uint32_t addr;
|
uint32_t addr;
|
||||||
char buf[BUFSIZ];
|
char buf[BUFSIZ];
|
||||||
|
@ -886,9 +887,6 @@ do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
|
||||||
* => display the VRF and table if specific
|
* => display the VRF and table if specific
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (use_json && !vrf_json)
|
|
||||||
json = json_object_new_object();
|
|
||||||
|
|
||||||
/* Show all routes. */
|
/* Show all routes. */
|
||||||
for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) {
|
for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) {
|
||||||
dest = rib_dest_from_rnode(rn);
|
dest = rib_dest_from_rnode(rn);
|
||||||
|
@ -961,28 +959,20 @@ do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
|
||||||
|
|
||||||
if (json_prefix) {
|
if (json_prefix) {
|
||||||
prefix2str(&rn->p, buf, sizeof(buf));
|
prefix2str(&rn->p, buf, sizeof(buf));
|
||||||
if (!vrf_json)
|
vty_json_key(vty, buf, &first_json);
|
||||||
json_object_object_add(json, buf, json_prefix);
|
vty_json_no_pretty(vty, json_prefix);
|
||||||
else
|
|
||||||
json_object_object_add(vrf_json, buf,
|
|
||||||
json_prefix);
|
|
||||||
json_prefix = NULL;
|
json_prefix = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (use_json)
|
||||||
* This is an extremely expensive operation at scale
|
vty_json_close(vty, first_json);
|
||||||
* and non-pretty reduces memory footprint significantly.
|
|
||||||
*/
|
|
||||||
if (use_json && !vrf_json) {
|
|
||||||
vty_json_no_pretty(vty, json);
|
|
||||||
json = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf,
|
static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf,
|
||||||
afi_t afi, bool use_fib, json_object *vrf_json,
|
afi_t afi, bool use_fib, bool use_json,
|
||||||
bool use_json, route_tag_t tag,
|
route_tag_t tag,
|
||||||
const struct prefix *longer_prefix_p,
|
const struct prefix *longer_prefix_p,
|
||||||
bool supernets_only, int type,
|
bool supernets_only, int type,
|
||||||
unsigned short ospf_instance_id, bool show_ng,
|
unsigned short ospf_instance_id, bool show_ng,
|
||||||
|
@ -1002,15 +992,15 @@ static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
do_show_ip_route(vty, zvrf_name(zvrf), afi, SAFI_UNICAST,
|
do_show_ip_route(vty, zvrf_name(zvrf), afi, SAFI_UNICAST,
|
||||||
use_fib, vrf_json, use_json, tag,
|
use_fib, use_json, tag, longer_prefix_p,
|
||||||
longer_prefix_p, supernets_only, type,
|
supernets_only, type, ospf_instance_id,
|
||||||
ospf_instance_id, zrt->tableid, show_ng, ctx);
|
zrt->tableid, show_ng, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
|
static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
|
||||||
safi_t safi, bool use_fib, json_object *vrf_json,
|
safi_t safi, bool use_fib, bool use_json,
|
||||||
bool use_json, route_tag_t tag,
|
route_tag_t tag,
|
||||||
const struct prefix *longer_prefix_p,
|
const struct prefix *longer_prefix_p,
|
||||||
bool supernets_only, int type,
|
bool supernets_only, int type,
|
||||||
unsigned short ospf_instance_id, uint32_t tableid,
|
unsigned short ospf_instance_id, uint32_t tableid,
|
||||||
|
@ -1045,7 +1035,7 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
do_show_route_helper(vty, zvrf, table, afi, use_fib, vrf_json, tag,
|
do_show_route_helper(vty, zvrf, table, afi, use_fib, tag,
|
||||||
longer_prefix_p, supernets_only, type,
|
longer_prefix_p, supernets_only, type,
|
||||||
ospf_instance_id, use_json, tableid, show_ng, ctx);
|
ospf_instance_id, use_json, tableid, show_ng, ctx);
|
||||||
|
|
||||||
|
@ -1744,13 +1734,13 @@ DEFPY (show_route,
|
||||||
"Nexthop Group Information\n")
|
"Nexthop Group Information\n")
|
||||||
{
|
{
|
||||||
afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
|
afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
|
||||||
|
bool first_vrf_json = true;
|
||||||
struct vrf *vrf;
|
struct vrf *vrf;
|
||||||
int type = 0;
|
int type = 0;
|
||||||
struct zebra_vrf *zvrf;
|
struct zebra_vrf *zvrf;
|
||||||
struct route_show_ctx ctx = {
|
struct route_show_ctx ctx = {
|
||||||
.multi = vrf_all || table_all,
|
.multi = vrf_all || table_all,
|
||||||
};
|
};
|
||||||
json_object *root_json = NULL;
|
|
||||||
|
|
||||||
if (!vrf_is_backend_netns()) {
|
if (!vrf_is_backend_netns()) {
|
||||||
if ((vrf_all || vrf_name) && (table || table_all)) {
|
if ((vrf_all || vrf_name) && (table || table_all)) {
|
||||||
|
@ -1772,43 +1762,30 @@ DEFPY (show_route,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vrf_all) {
|
if (vrf_all) {
|
||||||
if (!!json)
|
|
||||||
root_json = json_object_new_object();
|
|
||||||
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
|
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
|
||||||
json_object *vrf_json = NULL;
|
|
||||||
|
|
||||||
if ((zvrf = vrf->info) == NULL
|
if ((zvrf = vrf->info) == NULL
|
||||||
|| (zvrf->table[afi][SAFI_UNICAST] == NULL))
|
|| (zvrf->table[afi][SAFI_UNICAST] == NULL))
|
||||||
continue;
|
continue;
|
||||||
|
if (json)
|
||||||
if (!!json)
|
vty_json_key(vty, zvrf_name(zvrf),
|
||||||
vrf_json = json_object_new_object();
|
&first_vrf_json);
|
||||||
|
|
||||||
if (table_all)
|
if (table_all)
|
||||||
do_show_ip_route_all(vty, zvrf, afi, !!fib,
|
do_show_ip_route_all(vty, zvrf, afi, !!fib,
|
||||||
vrf_json, !!json, tag,
|
!!json, tag,
|
||||||
prefix_str ? prefix : NULL,
|
prefix_str ? prefix : NULL,
|
||||||
!!supernets_only, type,
|
!!supernets_only, type,
|
||||||
ospf_instance_id, !!ng,
|
ospf_instance_id, !!ng,
|
||||||
&ctx);
|
&ctx);
|
||||||
else
|
else
|
||||||
do_show_ip_route(vty, zvrf_name(zvrf), afi,
|
do_show_ip_route(vty, zvrf_name(zvrf), afi,
|
||||||
SAFI_UNICAST, !!fib, vrf_json,
|
SAFI_UNICAST, !!fib, !!json,
|
||||||
!!json, tag,
|
tag, prefix_str ? prefix : NULL,
|
||||||
prefix_str ? prefix : NULL,
|
|
||||||
!!supernets_only, type,
|
!!supernets_only, type,
|
||||||
ospf_instance_id, table, !!ng,
|
ospf_instance_id, table, !!ng,
|
||||||
&ctx);
|
&ctx);
|
||||||
|
|
||||||
if (!!json)
|
|
||||||
json_object_object_add(root_json,
|
|
||||||
zvrf_name(zvrf),
|
|
||||||
vrf_json);
|
|
||||||
}
|
|
||||||
if (!!json) {
|
|
||||||
vty_json_no_pretty(vty, root_json);
|
|
||||||
root_json = NULL;
|
|
||||||
}
|
}
|
||||||
|
if (json)
|
||||||
|
vty_json_close(vty, first_vrf_json);
|
||||||
} else {
|
} else {
|
||||||
vrf_id_t vrf_id = VRF_DEFAULT;
|
vrf_id_t vrf_id = VRF_DEFAULT;
|
||||||
|
|
||||||
|
@ -1823,13 +1800,13 @@ DEFPY (show_route,
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
|
|
||||||
if (table_all)
|
if (table_all)
|
||||||
do_show_ip_route_all(vty, zvrf, afi, !!fib, NULL, !!json,
|
do_show_ip_route_all(vty, zvrf, afi, !!fib, !!json, tag,
|
||||||
tag, prefix_str ? prefix : NULL,
|
prefix_str ? prefix : NULL,
|
||||||
!!supernets_only, type,
|
!!supernets_only, type,
|
||||||
ospf_instance_id, !!ng, &ctx);
|
ospf_instance_id, !!ng, &ctx);
|
||||||
else
|
else
|
||||||
do_show_ip_route(vty, vrf->name, afi, SAFI_UNICAST,
|
do_show_ip_route(vty, vrf->name, afi, SAFI_UNICAST,
|
||||||
!!fib, NULL, !!json, tag,
|
!!fib, !!json, tag,
|
||||||
prefix_str ? prefix : NULL,
|
prefix_str ? prefix : NULL,
|
||||||
!!supernets_only, type,
|
!!supernets_only, type,
|
||||||
ospf_instance_id, table, !!ng, &ctx);
|
ospf_instance_id, table, !!ng, &ctx);
|
||||||
|
|
Loading…
Reference in a new issue