From 7a7b0c13f9c86cdd1554a8fee1d0104f2e231881 Mon Sep 17 00:00:00 2001 From: Wesley Coakley Date: Mon, 18 May 2020 10:05:08 -0400 Subject: [PATCH 01/11] pbrd: json added to `show pbr interface [json]` Signed-off-by: Wesley Coakley --- pbrd/pbr_vty.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index a52c2d1e30..0d89010359 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -27,6 +27,7 @@ #include "nexthop_group.h" #include "nexthop_group_private.h" #include "log.h" +#include "json.h" #include "debug.h" #include "pbr.h" @@ -638,19 +639,28 @@ DEFPY(show_pbr_nexthop_group, DEFPY (show_pbr_interface, show_pbr_interface_cmd, - "show pbr interface [NAME$name]", + "show pbr interface [NAME$name] [json$json]", SHOW_STR PBR_STR "PBR Interface\n" - "PBR Interface Name\n") + "PBR Interface Name\n" + JSON_STR) { struct interface *ifp; struct vrf *vrf; struct pbr_interface *pbr_ifp; + json_object *j = NULL; + + if (json) + j = json_object_new_object(); RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { FOR_ALL_INTERFACES(vrf, ifp) { struct pbr_map *pbrm; + json_object *this_iface = NULL; + + if (j) + this_iface = json_object_new_object(); if (!ifp->info) continue; @@ -664,6 +674,22 @@ DEFPY (show_pbr_interface, continue; pbrm = pbrm_find(pbr_ifp->mapname); + + if (this_iface) { + json_object_string_add(this_iface, "name", + ifp->name); + json_object_int_add(this_iface, "index", + ifp->ifindex); + json_object_string_add(this_iface, "policy", + pbr_ifp->mapname); + json_object_boolean_add(this_iface, "valid", + pbrm); + + json_object_object_add(j, ifp->name, + this_iface); + continue; + } + vty_out(vty, " %s(%d) with pbr-policy %s", ifp->name, ifp->ifindex, pbr_ifp->mapname); if (!pbrm) @@ -672,6 +698,13 @@ DEFPY (show_pbr_interface, } } + if (j) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY)); + json_object_free(j); + } + return CMD_SUCCESS; } From 1a8cafe19c160b8c2e1c1d60b2b5faffb9567bc5 Mon Sep 17 00:00:00 2001 From: Wesley Coakley Date: Tue, 19 May 2020 13:15:21 -0500 Subject: [PATCH 02/11] pbrd: optional json for `show pbr map` Signed-off-by: Wesley Coakley --- doc/user/pbr.rst | 4 +- pbrd/pbr_vty.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 112 insertions(+), 3 deletions(-) diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index b9b28baced..b41a2ddacb 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -115,11 +115,13 @@ end destination. Not supported with NETNS VRF backend. -.. clicmd:: show pbr map [NAME] [detail] +.. clicmd:: show pbr map [NAME] [detail|json] Display pbr maps either all or by ``NAME``. If ``detail`` is set, it will give information about the rules unique ID used internally and some extra debugging information about install state for the nexthop/nexthop group. + Setting ``json`` will provide the same information in a predictable and + parsable format. .. _pbr-policy: diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 0d89010359..61882eb75b 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -591,6 +591,71 @@ static void vty_show_pbrms(struct vty *vty, } } +static void vty_json_pbrms(json_object *j, struct vty *vty, + const struct pbr_map_sequence *pbrms) +{ + json_object *jpbrm, *matches, *nexthop_group; + char *nhg_name = pbrms->nhgrp_name ? pbrms->nhgrp_name + : pbrms->internal_nhg_name; + char buf[PREFIX_STRLEN]; + char rbuf[64]; + + jpbrm = json_object_new_object(); + + if (pbrms->reason) + pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf)); + + json_object_int_add(jpbrm, "sequenceNumber", pbrms->seqno); + json_object_int_add(jpbrm, "ruleNumber", pbrms->ruleno); + + json_object_int_add(jpbrm, "identifier", pbrms->unique); + json_object_boolean_add(jpbrm, "vrfUnchanged", pbrms->vrf_unchanged); + + json_object_boolean_add(jpbrm, "installed", + pbr_nht_get_installed(nhg_name)); + json_object_string_add(jpbrm, "reason", pbrms->reason ? rbuf : "Valid"); + + if (pbrms->nhgrp_name || pbrms->mark) { + nexthop_group = json_object_new_object(); + + json_object_string_add(nexthop_group, "name", nhg_name); + + json_object_int_add(nexthop_group, "nhsInstalled", + pbrms->nhs_installed); + json_object_int_add(nexthop_group, "nhtInstalled", + pbr_nht_get_installed(nhg_name)); + + json_object_int_add(nexthop_group, "tableId", + pbr_nht_get_table(nhg_name)); + json_object_boolean_add(nexthop_group, "installed", + pbr_nht_get_installed(nhg_name)); + + json_object_object_add(jpbrm, "nexthopGroup", nexthop_group); + + } else if (pbrms->vrf_lookup) { + json_object_string_add(jpbrm, "vrf", pbrms->vrf_name); + } + + if (pbrms->src || pbrms->dst || pbrms->mark) { + matches = json_object_new_object(); + + if (pbrms->src) + json_object_string_add( + matches, "src", + prefix2str(pbrms->src, buf, sizeof(buf))); + if (pbrms->dst) + json_object_string_add( + matches, "dst", + prefix2str(pbrms->dst, buf, sizeof(buf))); + if (pbrms->mark) + json_object_int_add(matches, "mark", pbrms->mark); + + json_object_object_add(jpbrm, "matchRule", matches); + } + + json_object_array_add(j, jpbrm); +} + static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm, bool detail) { @@ -604,23 +669,65 @@ static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm, vty_show_pbrms(vty, pbrms, detail); } +static void vty_json_pbr_map(json_object *j, struct vty *vty, + const struct pbr_map *pbrm) +{ + struct pbr_map_sequence *pbrms; + struct listnode *node; + json_object *jpbrms; + + json_object_string_add(j, "name", pbrm->name); + json_object_boolean_add(j, "valid", pbrm->valid); + + jpbrms = json_object_new_array(); + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + vty_json_pbrms(jpbrms, vty, pbrms); + + json_object_object_add(j, "sequences", jpbrms); +} + DEFPY (show_pbr_map, show_pbr_map_cmd, - "show pbr map [NAME$name] [detail$detail]", + "show pbr map [NAME$name] [detail$detail|json$json]", SHOW_STR PBR_STR "PBR Map\n" "PBR Map Name\n" - "Detailed information\n") + "Detailed information\n" + JSON_STR) { struct pbr_map *pbrm; + json_object *j = NULL; + + if (json) + j = json_object_new_object(); RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + json_object *this_map = NULL; if (name && strcmp(name, pbrm->name) != 0) continue; + if (j) + this_map = json_object_new_object(); + + if (this_map) { + vty_json_pbr_map(this_map, vty, pbrm); + + json_object_object_add(j, pbrm->name, this_map); + continue; + } + vty_show_pbr_map(vty, pbrm, detail); } + + if (j) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY)); + json_object_free(j); + } + return CMD_SUCCESS; } From 010dd8edcb4e1fec3c252570efbf98ac1a02a34a Mon Sep 17 00:00:00 2001 From: Wesley Coakley Date: Wed, 20 May 2020 10:49:54 -0400 Subject: [PATCH 03/11] pbrd, lib: opt. json for `show pbr nexthop-group` Signed-off-by: Wesley Coakley --- doc/user/pbr.rst | 5 +++-- lib/nexthop_group.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ lib/nexthop_group.h | 3 +++ pbrd/pbr_nht.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ pbrd/pbr_nht.h | 2 ++ pbrd/pbr_vty.c | 22 +++++++++++++++--- 6 files changed, 135 insertions(+), 5 deletions(-) diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index b41a2ddacb..2815b040f2 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -56,10 +56,11 @@ listing of ECMP nexthops used to forward packets for when a pbr-map is matched. Showing Nexthop Group Information --------------------------------- -.. clicmd:: show pbr nexthop-groups [NAME] +.. clicmd:: show pbr nexthop-groups [NAME] [json] Display information on a PBR nexthop-group. If ``NAME`` is omitted, all - nexthop groups are shown. + nexthop groups are shown. Setting ``json`` will provide the same information + in a predictable and parsable format. .. _pbr-maps: diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index c23c57d2e1..f9e337d753 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -996,6 +996,60 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) vty_out(vty, "\n"); } +void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh) +{ + char buf[100]; + struct vrf *vrf; + + switch (nh->type) { + case NEXTHOP_TYPE_IFINDEX: + json_object_string_add(j, "nexthop", + ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_IPV4: + json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4)); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4)); + json_object_string_add(j, "vrfId", + ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + json_object_string_add( + j, "nexthop", + inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + json_object_string_add( + j, "nexthop", + inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); + json_object_string_add(j, "vrfId", + ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + + if (nh->vrf_id != VRF_DEFAULT) { + vrf = vrf_lookup_by_id(nh->vrf_id); + json_object_string_add(j, "nexthopVrf", vrf->name); + } + + if (nh->nh_label && nh->nh_label->num_labels > 0) { + char buf[200]; + + mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label, + buf, sizeof(buf), 0); + json_object_string_add(j, "label", buf); + } + + if (nh->weight) + json_object_int_add(j, "weight", nh->weight); + + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) + json_object_int_add(j, "backupIdx", nh->backup_idx); +} + static void nexthop_group_write_nexthop_internal(struct vty *vty, struct nexthop_hold *nh) { diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index 3a5a1299c1..9888dad982 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -22,6 +22,7 @@ #define __NEXTHOP_GROUP__ #include +#include "json.h" #ifdef __cplusplus extern "C" { @@ -136,6 +137,8 @@ extern struct nexthop_group_cmd *nhgc_find(const char *name); extern void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh); +extern void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh); + /* Return the number of nexthops in this nhg */ extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg); extern uint8_t diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 2f3591ac8d..ae7a8017c2 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -1030,8 +1030,22 @@ static void pbr_nht_show_nhg_nexthops(struct hash_bucket *b, void *data) nexthop_group_write_nexthop(vty, pnhc->nexthop); } +static void pbr_nht_json_nhg_nexthops(struct hash_bucket *b, void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + json_object *all_hops = data; + json_object *this_hop; + + this_hop = json_object_new_object(); + json_object_boolean_add(this_hop, "valid", pnhc->valid); + nexthop_group_json_nexthop(this_hop, pnhc->nexthop); + + json_object_array_add(all_hops, this_hop); +} + struct pbr_nht_show { struct vty *vty; + json_object *json; const char *name; }; @@ -1051,6 +1065,36 @@ static void pbr_nht_show_nhg(struct hash_bucket *b, void *data) hash_iterate(pnhgc->nhh, pbr_nht_show_nhg_nexthops, vty); } +static void pbr_nht_json_nhg(struct hash_bucket *b, void *data) +{ + struct pbr_nexthop_group_cache *pnhgc = b->data; + struct pbr_nht_show *pns = data; + json_object *j, *this_group, *group_hops; + + if (pns->name && strcmp(pns->name, pnhgc->name) != 0) + return; + + j = pns->json; + this_group = json_object_new_object(); + + if (!j || !this_group) + return; + + json_object_string_add(this_group, "name", pnhgc->name); + json_object_int_add(this_group, "id", pnhgc->table_id); + json_object_boolean_add(this_group, "valid", pnhgc->valid); + json_object_boolean_add(this_group, "installed", pnhgc->installed); + + group_hops = json_object_new_array(); + + if (group_hops) { + hash_iterate(pnhgc->nhh, pbr_nht_json_nhg_nexthops, group_hops); + json_object_object_add(this_group, "nexthops", group_hops); + } + + json_object_object_add(j, pnhgc->name, this_group); +} + void pbr_nht_show_nexthop_group(struct vty *vty, const char *name) { struct pbr_nht_show pns; @@ -1061,6 +1105,16 @@ void pbr_nht_show_nexthop_group(struct vty *vty, const char *name) hash_iterate(pbr_nhg_hash, pbr_nht_show_nhg, &pns); } +void pbr_nht_json_nexthop_group(json_object *j, const char *name) +{ + struct pbr_nht_show pns; + + pns.name = name; + pns.json = j; + + hash_iterate(pbr_nhg_hash, pbr_nht_json_nhg, &pns); +} + void pbr_nht_init(void) { pbr_nhg_hash = hash_create_size( diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h index 2533942547..cbcf71d2f5 100644 --- a/pbrd/pbr_nht.h +++ b/pbrd/pbr_nht.h @@ -24,6 +24,7 @@ #include #include "pbr_map.h" +#include "json.h" #define PBR_NHC_NAMELEN PBR_MAP_NAMELEN + 10 @@ -112,6 +113,7 @@ extern char *pbr_nht_nexthop_make_name(char *name, size_t l, uint32_t seqno, char *buffer); extern void pbr_nht_show_nexthop_group(struct vty *vty, const char *name); +extern void pbr_nht_json_nexthop_group(json_object *j, const char *name); /* * When we get a callback from zebra about a nexthop changing diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 61882eb75b..f94a4d43b0 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -733,13 +733,29 @@ DEFPY (show_pbr_map, DEFPY(show_pbr_nexthop_group, show_pbr_nexthop_group_cmd, - "show pbr nexthop-groups [WORD$word]", + "show pbr nexthop-groups [WORD$word] [json$json]", SHOW_STR PBR_STR "Nexthop Groups\n" - "Optional Name of the nexthop group\n") + "Optional Name of the nexthop group\n" + JSON_STR) { - pbr_nht_show_nexthop_group(vty, word); + json_object *j = NULL; + + if (json) + j = json_object_new_object(); + + if (j) { + pbr_nht_json_nexthop_group(j, word); + + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY)); + + json_object_free(j); + } else + pbr_nht_show_nexthop_group(vty, word); + return CMD_SUCCESS; } From 81c0078ef4d0bb6471447e6eecd28663e0620cb3 Mon Sep 17 00:00:00 2001 From: Wesley Coakley Date: Thu, 21 May 2020 15:18:34 -0400 Subject: [PATCH 04/11] pbrd, lib: verbosity++ for json `show` directives Increased the verbosity of the json keys and flattened the returned structure by removing superfluous keys. Signed-off-by: Wesley Coakley --- lib/nexthop_group.c | 12 ++++----- pbrd/pbr_nht.c | 8 +++--- pbrd/pbr_vty.c | 62 +++++++++++++++++++-------------------------- 3 files changed, 36 insertions(+), 46 deletions(-) diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index f9e337d753..ab04fb904e 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -1003,25 +1003,25 @@ void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh) switch (nh->type) { case NEXTHOP_TYPE_IFINDEX: - json_object_string_add(j, "nexthop", + json_object_string_add(j, "target", ifindex2ifname(nh->ifindex, nh->vrf_id)); break; case NEXTHOP_TYPE_IPV4: - json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4)); + json_object_string_add(j, "target", inet_ntoa(nh->gate.ipv4)); break; case NEXTHOP_TYPE_IPV4_IFINDEX: - json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4)); + json_object_string_add(j, "target", inet_ntoa(nh->gate.ipv4)); json_object_string_add(j, "vrfId", ifindex2ifname(nh->ifindex, nh->vrf_id)); break; case NEXTHOP_TYPE_IPV6: json_object_string_add( - j, "nexthop", + j, "target", inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); break; case NEXTHOP_TYPE_IPV6_IFINDEX: json_object_string_add( - j, "nexthop", + j, "target", inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); json_object_string_add(j, "vrfId", ifindex2ifname(nh->ifindex, nh->vrf_id)); @@ -1032,7 +1032,7 @@ void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh) if (nh->vrf_id != VRF_DEFAULT) { vrf = vrf_lookup_by_id(nh->vrf_id); - json_object_string_add(j, "nexthopVrf", vrf->name); + json_object_string_add(j, "targetVrf", vrf->name); } if (nh->nh_label && nh->nh_label->num_labels > 0) { diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index ae7a8017c2..30135dd9a4 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -1037,8 +1037,8 @@ static void pbr_nht_json_nhg_nexthops(struct hash_bucket *b, void *data) json_object *this_hop; this_hop = json_object_new_object(); - json_object_boolean_add(this_hop, "valid", pnhc->valid); nexthop_group_json_nexthop(this_hop, pnhc->nexthop); + json_object_boolean_add(this_hop, "isValid", pnhc->valid); json_object_array_add(all_hops, this_hop); } @@ -1080,10 +1080,10 @@ static void pbr_nht_json_nhg(struct hash_bucket *b, void *data) if (!j || !this_group) return; - json_object_string_add(this_group, "name", pnhgc->name); json_object_int_add(this_group, "id", pnhgc->table_id); - json_object_boolean_add(this_group, "valid", pnhgc->valid); - json_object_boolean_add(this_group, "installed", pnhgc->installed); + json_object_string_add(this_group, "name", pnhgc->name); + json_object_boolean_add(this_group, "isValid", pnhgc->valid); + json_object_boolean_add(this_group, "isInstalled", pnhgc->installed); group_hops = json_object_new_array(); diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index f94a4d43b0..505c600184 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -594,7 +594,7 @@ static void vty_show_pbrms(struct vty *vty, static void vty_json_pbrms(json_object *j, struct vty *vty, const struct pbr_map_sequence *pbrms) { - json_object *jpbrm, *matches, *nexthop_group; + json_object *jpbrm, *nexthop_group; char *nhg_name = pbrms->nhgrp_name ? pbrms->nhgrp_name : pbrms->internal_nhg_name; char buf[PREFIX_STRLEN]; @@ -602,56 +602,46 @@ static void vty_json_pbrms(json_object *j, struct vty *vty, jpbrm = json_object_new_object(); + json_object_int_add(jpbrm, "id", pbrms->unique); + if (pbrms->reason) pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf)); json_object_int_add(jpbrm, "sequenceNumber", pbrms->seqno); json_object_int_add(jpbrm, "ruleNumber", pbrms->ruleno); - - json_object_int_add(jpbrm, "identifier", pbrms->unique); json_object_boolean_add(jpbrm, "vrfUnchanged", pbrms->vrf_unchanged); - - json_object_boolean_add(jpbrm, "installed", + json_object_boolean_add(jpbrm, "isInstalled", pbr_nht_get_installed(nhg_name)); - json_object_string_add(jpbrm, "reason", pbrms->reason ? rbuf : "Valid"); + json_object_string_add(jpbrm, "installedReason", + pbrms->reason ? rbuf : "Valid"); - if (pbrms->nhgrp_name || pbrms->mark) { + if (nhg_name) { nexthop_group = json_object_new_object(); - json_object_string_add(nexthop_group, "name", nhg_name); - - json_object_int_add(nexthop_group, "nhsInstalled", - pbrms->nhs_installed); - json_object_int_add(nexthop_group, "nhtInstalled", - pbr_nht_get_installed(nhg_name)); - - json_object_int_add(nexthop_group, "tableId", + json_object_int_add(nexthop_group, "id", pbr_nht_get_table(nhg_name)); - json_object_boolean_add(nexthop_group, "installed", + json_object_string_add(nexthop_group, "name", nhg_name); + json_object_boolean_add(nexthop_group, "isInstalled", pbr_nht_get_installed(nhg_name)); + json_object_int_add(nexthop_group, "isInstalledInternally", + pbrms->nhs_installed); json_object_object_add(jpbrm, "nexthopGroup", nexthop_group); - - } else if (pbrms->vrf_lookup) { - json_object_string_add(jpbrm, "vrf", pbrms->vrf_name); } - if (pbrms->src || pbrms->dst || pbrms->mark) { - matches = json_object_new_object(); + if (pbrms->vrf_lookup) + json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name); - if (pbrms->src) - json_object_string_add( - matches, "src", - prefix2str(pbrms->src, buf, sizeof(buf))); - if (pbrms->dst) - json_object_string_add( - matches, "dst", - prefix2str(pbrms->dst, buf, sizeof(buf))); - if (pbrms->mark) - json_object_int_add(matches, "mark", pbrms->mark); - - json_object_object_add(jpbrm, "matchRule", matches); - } + if (pbrms->src) + json_object_string_add( + jpbrm, "matchSrc", + prefix2str(pbrms->src, buf, sizeof(buf))); + if (pbrms->dst) + json_object_string_add( + jpbrm, "matchDst", + prefix2str(pbrms->dst, buf, sizeof(buf))); + if (pbrms->mark) + json_object_int_add(jpbrm, "matchMark", pbrms->mark); json_object_array_add(j, jpbrm); } @@ -677,7 +667,7 @@ static void vty_json_pbr_map(json_object *j, struct vty *vty, json_object *jpbrms; json_object_string_add(j, "name", pbrm->name); - json_object_boolean_add(j, "valid", pbrm->valid); + json_object_boolean_add(j, "isValid", pbrm->valid); jpbrms = json_object_new_array(); @@ -805,7 +795,7 @@ DEFPY (show_pbr_interface, ifp->ifindex); json_object_string_add(this_iface, "policy", pbr_ifp->mapname); - json_object_boolean_add(this_iface, "valid", + json_object_boolean_add(this_iface, "isValid", pbrm); json_object_object_add(j, ifp->name, From dadba1a23f83e23c7d7f2669bcbac319bd7d4674 Mon Sep 17 00:00:00 2001 From: Wesley Coakley Date: Thu, 21 May 2020 15:33:06 -0400 Subject: [PATCH 05/11] pbrd: `show` directives give arrays where appropos The new json output for the `show pbr` directives return arrays instead of associative arrays, which are more meaningful in this context Signed-off-by: Wesley Coakley --- pbrd/pbr_nht.c | 2 +- pbrd/pbr_vty.c | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 30135dd9a4..4836a58792 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -1092,7 +1092,7 @@ static void pbr_nht_json_nhg(struct hash_bucket *b, void *data) json_object_object_add(this_group, "nexthops", group_hops); } - json_object_object_add(j, pnhgc->name, this_group); + json_object_array_add(j, this_group); } void pbr_nht_show_nexthop_group(struct vty *vty, const char *name) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 505c600184..ae35eb23af 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -691,7 +691,7 @@ DEFPY (show_pbr_map, json_object *j = NULL; if (json) - j = json_object_new_object(); + j = json_object_new_array(); RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { json_object *this_map = NULL; @@ -704,7 +704,7 @@ DEFPY (show_pbr_map, if (this_map) { vty_json_pbr_map(this_map, vty, pbrm); - json_object_object_add(j, pbrm->name, this_map); + json_object_array_add(j, this_map); continue; } @@ -733,7 +733,7 @@ DEFPY(show_pbr_nexthop_group, json_object *j = NULL; if (json) - j = json_object_new_object(); + j = json_object_new_array(); if (j) { pbr_nht_json_nexthop_group(j, word); @@ -765,7 +765,7 @@ DEFPY (show_pbr_interface, json_object *j = NULL; if (json) - j = json_object_new_object(); + j = json_object_new_array(); RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { FOR_ALL_INTERFACES(vrf, ifp) { @@ -798,8 +798,7 @@ DEFPY (show_pbr_interface, json_object_boolean_add(this_iface, "isValid", pbrm); - json_object_object_add(j, ifp->name, - this_iface); + json_object_array_add(j, this_iface); continue; } From 9667c597e919119d01a65160eac74f3b75725bef Mon Sep 17 00:00:00 2001 From: Wesley Coakley Date: Fri, 22 May 2020 09:07:00 -0400 Subject: [PATCH 06/11] doc: document `show pbr interface` directive Signed-off-by: Wesley Coakley --- doc/user/pbr.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 2815b040f2..b07cc259d7 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -144,6 +144,24 @@ causes the policy to be installed into the kernel. even if one is on the master. Each must have the PBR map explicitly added to the interface. +.. clicmd:: show pbr interface [NAME] [json] + + Enumerates all interfaces which ``pbrd`` is keeping track of. Passing + ``json`` will return an array of interfaces; each returned interface will + adhere to the JSON schema below: + + +---------+----------------------------+---------+ + | Key | Description | Type | + +=========+============================+=========+ + | name | Interface name | String | + +---------+----------------------------+---------+ + | index | Device Index | Integer | + +---------+----------------------------+---------+ + | policy | PBR map for this interface | String | + +---------+----------------------------+---------+ + | isValid | Is the map well-formed? | Boolean | + +---------+----------------------------+---------+ + .. _pbr-details: PBR Details From 144fd50f64f60590bc47efe28b64c7b406a073ab Mon Sep 17 00:00:00 2001 From: Wesley Coakley Date: Fri, 22 May 2020 11:36:52 -0400 Subject: [PATCH 07/11] doc: document json for remaining `show pbr` cmds Signed-off-by: Wesley Coakley --- doc/user/pbr.rst | 88 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 4 deletions(-) diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index b07cc259d7..af4c7c932b 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -59,8 +59,33 @@ Showing Nexthop Group Information .. clicmd:: show pbr nexthop-groups [NAME] [json] Display information on a PBR nexthop-group. If ``NAME`` is omitted, all - nexthop groups are shown. Setting ``json`` will provide the same information - in a predictable and parsable format. + nexthop groups are shown. Setting ``json`` will provide the same + information in an array of objects which obey the schema below: + + +-------------+----------------------------+---------+ + | Key | Description | Type | + +=============+============================+=========+ + | id | Unique ID | Integer | + +-------------+----------------------------+---------+ + | name | Name of this group | String | + +-------------+----------------------------+---------+ + | isValud | Is this group well-formed? | Boolean | + +-------------+----------------------------+---------+ + | isInstalled | ... and is it installed? | Boolean | + +-------------+----------------------------+---------+ + | nexthops | Nexthops within this group | Array | + +-------------+----------------------------+---------+ + + Each element within ``nexthops`` describes a single target within this + group, and its structure is described by the JSON below: + + +---------+------------------------------+---------+ + | Key | Description | Type | + +=========+==============================+=========+ + | target | Name of this nexthop | String | + +---------+------------------------------+---------+ + | isValid | Is this nexthop well-formed? | Boolean | + +---------+------------------------------+---------+ .. _pbr-maps: @@ -121,8 +146,63 @@ end destination. Display pbr maps either all or by ``NAME``. If ``detail`` is set, it will give information about the rules unique ID used internally and some extra debugging information about install state for the nexthop/nexthop group. - Setting ``json`` will provide the same information in a predictable and - parsable format. + Setting ``json`` will provide the same information in an array of objects + which obey the schema below: + + +--------------+--------------------------------+---------+ + | Key | Description | Type | + +==============+================================+=========+ + | name | Map name | String | + +--------------+--------------------------------+---------+ + | isValid | Is the map well-formed? | Boolean | + +--------------+--------------------------------+---------+ + | sequences | Rules to match packets against | Array | + +--------------+--------------------------------+---------+ + + Each element of the ``sequences`` array is composed of a handful of objects + representing the policies associated with this map. Each policy is + described as below (not all fields are required): + + +-----------------+-------------------------------------------+---------+ + | Key | Description | Type | + +=================+===========================================+=========+ + | id | Unique ID | Integer | + +-----------------+-------------------------------------------+---------+ + | sequenceNumber | Order of this policy within the map | Integer | + +-----------------+-------------------------------------------+---------+ + | ruleNumber | Rule number to install into | Integer | + +-----------------+-------------------------------------------+---------+ + | vrfUnchanged | Use interface's VRF | Boolean | + +-----------------+-------------------------------------------+---------+ + | isInstalled | Is this policy installed? | Boolean | + +-----------------+-------------------------------------------+---------+ + | installedReason | Why (or why not?) | String | + +-----------------+-------------------------------------------+---------+ + | matchSrc | Match packets with this source address | String | + +-----------------+-------------------------------------------+---------+ + | matchDst | ... or with this destination address | String | + +-----------------+-------------------------------------------+---------+ + | matchMark | ... or with this marker | Integer | + +-----------------+-------------------------------------------+---------+ + | vrfName | Associated VRF (if relevant) | String | + +-----------------+-------------------------------------------+---------+ + | nexthopGroup | This policy's nexthop group (if relevant) | Object | + +-----------------+-------------------------------------------+---------+ + + Finally, the ``nexthopGroup`` object above cotains information we know + about the configured nexthop for this policy: + + +-----------------------+--------------------------------------+---------+ + | Key | Description | Type | + +=======================+======================================+=========+ + | id | Nexthop table ID | Integer | + +-----------------------+--------------------------------------+---------+ + | name | Name of the nexthop group | String | + +-----------------------+--------------------------------------+---------+ + | isInstalled | Is this nexthop group installed? | Boolean | + +-----------------------+--------------------------------------+---------+ + | isInstalledInternally | Do we think this group is installed? | Integer | + +-----------------------+--------------------------------------+---------+ .. _pbr-policy: From f9368f896b7d4ed8c9f7e4d6a271c7f839c53022 Mon Sep 17 00:00:00 2001 From: Wesley Coakley Date: Fri, 22 May 2020 11:47:18 -0400 Subject: [PATCH 08/11] pbrd, doc: pbr map json `sequences` -> `policies` Semantics Signed-off-by: Wesley Coakley --- doc/user/pbr.rst | 4 ++-- pbrd/pbr_vty.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index af4c7c932b..a99f12ec3f 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -156,10 +156,10 @@ end destination. +--------------+--------------------------------+---------+ | isValid | Is the map well-formed? | Boolean | +--------------+--------------------------------+---------+ - | sequences | Rules to match packets against | Array | + | policies | Rules to match packets against | Array | +--------------+--------------------------------+---------+ - Each element of the ``sequences`` array is composed of a handful of objects + Each element of the ``policies`` array is composed of a handful of objects representing the policies associated with this map. Each policy is described as below (not all fields are required): diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index ae35eb23af..819d8a8f26 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -674,7 +674,7 @@ static void vty_json_pbr_map(json_object *j, struct vty *vty, for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) vty_json_pbrms(jpbrms, vty, pbrms); - json_object_object_add(j, "sequences", jpbrms); + json_object_object_add(j, "policies", jpbrms); } DEFPY (show_pbr_map, From 3e81618d0ce5140978462a63827bc28c76d47e4a Mon Sep 17 00:00:00 2001 From: Wesley Coakley Date: Tue, 26 May 2020 16:44:46 -0400 Subject: [PATCH 09/11] pbrd, lib, doc: fix new `show` json key semantics Revise new `show pbr` keys to be consistent with existing json in other daemons target->nexthop id->tableId (where relevant) isValid->valid isInstalled->installed Signed-off-by: Wesley Coakley --- doc/user/pbr.rst | 94 ++++++++++++++++++++++----------------------- lib/nexthop_group.c | 10 ++--- pbrd/pbr_nht.c | 6 +-- pbrd/pbr_vty.c | 12 +++--- 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index a99f12ec3f..149949e863 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -62,19 +62,19 @@ Showing Nexthop Group Information nexthop groups are shown. Setting ``json`` will provide the same information in an array of objects which obey the schema below: - +-------------+----------------------------+---------+ - | Key | Description | Type | - +=============+============================+=========+ - | id | Unique ID | Integer | - +-------------+----------------------------+---------+ - | name | Name of this group | String | - +-------------+----------------------------+---------+ - | isValud | Is this group well-formed? | Boolean | - +-------------+----------------------------+---------+ - | isInstalled | ... and is it installed? | Boolean | - +-------------+----------------------------+---------+ - | nexthops | Nexthops within this group | Array | - +-------------+----------------------------+---------+ + +-----------+----------------------------+---------+ + | Key | Description | Type | + +===========+============================+=========+ + | id | Unique ID | Integer | + +-----------+----------------------------+---------+ + | name | Name of this group | String | + +-----------+----------------------------+---------+ + | valid | Is this group well-formed? | Boolean | + +-----------+----------------------------+---------+ + | installed | ... and is it installed? | Boolean | + +-----------+----------------------------+---------+ + | nexthops | Nexthops within this group | Array | + +-----------+----------------------------+---------+ Each element within ``nexthops`` describes a single target within this group, and its structure is described by the JSON below: @@ -82,9 +82,9 @@ Showing Nexthop Group Information +---------+------------------------------+---------+ | Key | Description | Type | +=========+==============================+=========+ - | target | Name of this nexthop | String | + | nexthop | Name of this nexthop | String | +---------+------------------------------+---------+ - | isValid | Is this nexthop well-formed? | Boolean | + | valid | Is this nexthop well-formed? | Boolean | +---------+------------------------------+---------+ .. _pbr-maps: @@ -149,15 +149,15 @@ end destination. Setting ``json`` will provide the same information in an array of objects which obey the schema below: - +--------------+--------------------------------+---------+ - | Key | Description | Type | - +==============+================================+=========+ - | name | Map name | String | - +--------------+--------------------------------+---------+ - | isValid | Is the map well-formed? | Boolean | - +--------------+--------------------------------+---------+ - | policies | Rules to match packets against | Array | - +--------------+--------------------------------+---------+ + +----------+--------------------------------+---------+ + | Key | Description | Type | + +==========+================================+=========+ + | name | Map name | String | + +----------+--------------------------------+---------+ + | valid | Is the map well-formed? | Boolean | + +----------+--------------------------------+---------+ + | policies | Rules to match packets against | Array | + +----------+--------------------------------+---------+ Each element of the ``policies`` array is composed of a handful of objects representing the policies associated with this map. Each policy is @@ -174,7 +174,7 @@ end destination. +-----------------+-------------------------------------------+---------+ | vrfUnchanged | Use interface's VRF | Boolean | +-----------------+-------------------------------------------+---------+ - | isInstalled | Is this policy installed? | Boolean | + | installed | Is this policy installed? | Boolean | +-----------------+-------------------------------------------+---------+ | installedReason | Why (or why not?) | String | +-----------------+-------------------------------------------+---------+ @@ -192,17 +192,17 @@ end destination. Finally, the ``nexthopGroup`` object above cotains information we know about the configured nexthop for this policy: - +-----------------------+--------------------------------------+---------+ - | Key | Description | Type | - +=======================+======================================+=========+ - | id | Nexthop table ID | Integer | - +-----------------------+--------------------------------------+---------+ - | name | Name of the nexthop group | String | - +-----------------------+--------------------------------------+---------+ - | isInstalled | Is this nexthop group installed? | Boolean | - +-----------------------+--------------------------------------+---------+ - | isInstalledInternally | Do we think this group is installed? | Integer | - +-----------------------+--------------------------------------+---------+ + +---------------------+--------------------------------------+---------+ + | Key | Description | Type | + +=====================+======================================+=========+ + | tableId | Nexthop table ID | Integer | + +---------------------+--------------------------------------+---------+ + | name | Name of the nexthop group | String | + +---------------------+--------------------------------------+---------+ + | installed | Is this nexthop group installed? | Boolean | + +---------------------+--------------------------------------+---------+ + | installedInternally | Do we think this group is installed? | Integer | + +---------------------+--------------------------------------+---------+ .. _pbr-policy: @@ -230,17 +230,17 @@ causes the policy to be installed into the kernel. ``json`` will return an array of interfaces; each returned interface will adhere to the JSON schema below: - +---------+----------------------------+---------+ - | Key | Description | Type | - +=========+============================+=========+ - | name | Interface name | String | - +---------+----------------------------+---------+ - | index | Device Index | Integer | - +---------+----------------------------+---------+ - | policy | PBR map for this interface | String | - +---------+----------------------------+---------+ - | isValid | Is the map well-formed? | Boolean | - +---------+----------------------------+---------+ + +--------+----------------------------+---------+ + | Key | Description | Type | + +========+============================+=========+ + | name | Interface name | String | + +--------+----------------------------+---------+ + | index | Device Index | Integer | + +--------+----------------------------+---------+ + | policy | PBR map for this interface | String | + +--------+----------------------------+---------+ + | valid | Is the map well-formed? | Boolean | + +--------+----------------------------+---------+ .. _pbr-details: diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index ab04fb904e..c62096a126 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -1003,25 +1003,25 @@ void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh) switch (nh->type) { case NEXTHOP_TYPE_IFINDEX: - json_object_string_add(j, "target", + json_object_string_add(j, "nexthop", ifindex2ifname(nh->ifindex, nh->vrf_id)); break; case NEXTHOP_TYPE_IPV4: - json_object_string_add(j, "target", inet_ntoa(nh->gate.ipv4)); + json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4)); break; case NEXTHOP_TYPE_IPV4_IFINDEX: - json_object_string_add(j, "target", inet_ntoa(nh->gate.ipv4)); + json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4)); json_object_string_add(j, "vrfId", ifindex2ifname(nh->ifindex, nh->vrf_id)); break; case NEXTHOP_TYPE_IPV6: json_object_string_add( - j, "target", + j, "nexthop", inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); break; case NEXTHOP_TYPE_IPV6_IFINDEX: json_object_string_add( - j, "target", + j, "nexthop", inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); json_object_string_add(j, "vrfId", ifindex2ifname(nh->ifindex, nh->vrf_id)); diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 4836a58792..98be958fce 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -1038,7 +1038,7 @@ static void pbr_nht_json_nhg_nexthops(struct hash_bucket *b, void *data) this_hop = json_object_new_object(); nexthop_group_json_nexthop(this_hop, pnhc->nexthop); - json_object_boolean_add(this_hop, "isValid", pnhc->valid); + json_object_boolean_add(this_hop, "valid", pnhc->valid); json_object_array_add(all_hops, this_hop); } @@ -1082,8 +1082,8 @@ static void pbr_nht_json_nhg(struct hash_bucket *b, void *data) json_object_int_add(this_group, "id", pnhgc->table_id); json_object_string_add(this_group, "name", pnhgc->name); - json_object_boolean_add(this_group, "isValid", pnhgc->valid); - json_object_boolean_add(this_group, "isInstalled", pnhgc->installed); + json_object_boolean_add(this_group, "valid", pnhgc->valid); + json_object_boolean_add(this_group, "installed", pnhgc->installed); group_hops = json_object_new_array(); diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 819d8a8f26..54029206cc 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -610,7 +610,7 @@ static void vty_json_pbrms(json_object *j, struct vty *vty, json_object_int_add(jpbrm, "sequenceNumber", pbrms->seqno); json_object_int_add(jpbrm, "ruleNumber", pbrms->ruleno); json_object_boolean_add(jpbrm, "vrfUnchanged", pbrms->vrf_unchanged); - json_object_boolean_add(jpbrm, "isInstalled", + json_object_boolean_add(jpbrm, "installed", pbr_nht_get_installed(nhg_name)); json_object_string_add(jpbrm, "installedReason", pbrms->reason ? rbuf : "Valid"); @@ -618,12 +618,12 @@ static void vty_json_pbrms(json_object *j, struct vty *vty, if (nhg_name) { nexthop_group = json_object_new_object(); - json_object_int_add(nexthop_group, "id", + json_object_int_add(nexthop_group, "tableId", pbr_nht_get_table(nhg_name)); json_object_string_add(nexthop_group, "name", nhg_name); - json_object_boolean_add(nexthop_group, "isInstalled", + json_object_boolean_add(nexthop_group, "installed", pbr_nht_get_installed(nhg_name)); - json_object_int_add(nexthop_group, "isInstalledInternally", + json_object_int_add(nexthop_group, "installedInternally", pbrms->nhs_installed); json_object_object_add(jpbrm, "nexthopGroup", nexthop_group); @@ -667,7 +667,7 @@ static void vty_json_pbr_map(json_object *j, struct vty *vty, json_object *jpbrms; json_object_string_add(j, "name", pbrm->name); - json_object_boolean_add(j, "isValid", pbrm->valid); + json_object_boolean_add(j, "valid", pbrm->valid); jpbrms = json_object_new_array(); @@ -795,7 +795,7 @@ DEFPY (show_pbr_interface, ifp->ifindex); json_object_string_add(this_iface, "policy", pbr_ifp->mapname); - json_object_boolean_add(this_iface, "isValid", + json_object_boolean_add(this_iface, "valid", pbrm); json_object_array_add(j, this_iface); From 223f87f4db7bf7d0483dbd8a7ebd51e05acea3ec Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sat, 23 May 2020 13:24:38 -0400 Subject: [PATCH 10/11] tests: Setup very basic pbr tests for our topology tests Just a simple setup for pbr to prove it starts. Once the json code for pbr gets in we can add more. Signed-off-by: Donald Sharp --- tests/topotests/all-protocol-startup/r1/pbrd.conf | 10 ++++++++++ .../all-protocol-startup/test_all_protocol_startup.py | 1 + tests/topotests/lib/topogen.py | 2 ++ tests/topotests/lib/topotest.py | 1 + 4 files changed, 14 insertions(+) create mode 100644 tests/topotests/all-protocol-startup/r1/pbrd.conf diff --git a/tests/topotests/all-protocol-startup/r1/pbrd.conf b/tests/topotests/all-protocol-startup/r1/pbrd.conf new file mode 100644 index 0000000000..360fb13a1b --- /dev/null +++ b/tests/topotests/all-protocol-startup/r1/pbrd.conf @@ -0,0 +1,10 @@ +log file pbrd.log + +nexthop-group A + nexthop 192.168.161.4 +! +pbr-map FOO seq 10 + match dst-ip 4.5.6.7/32 + match src-ip 6.7.8.8/32 + set nexthop-group A +! \ No newline at end of file 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 fb211957a7..14e00b9664 100755 --- a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py +++ b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py @@ -123,6 +123,7 @@ def setup_module(module): net['r%s' % i].loadConf('sharpd') net['r%s' % i].loadConf('nhrpd', '%s/r%s/nhrpd.conf' % (thisDir, i)) net['r%s' % i].loadConf('babeld', '%s/r%s/babeld.conf' % (thisDir, i)) + net['r%s' % i].loadConf('pbrd', '%s/r%s/pbrd.conf' % (thisDir, i)) net['r%s' % i].startRouter() # For debugging after starting Quagga/FRR daemons, uncomment the next line diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 673d65376f..414dc17874 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -556,6 +556,7 @@ class TopoRouter(TopoGear): RD_BFD = 13 RD_SHARP = 14 RD_BABEL = 15 + RD_PBRD = 16 RD = { RD_ZEBRA: "zebra", RD_RIP: "ripd", @@ -572,6 +573,7 @@ class TopoRouter(TopoGear): RD_BFD: "bfdd", RD_SHARP: "sharpd", RD_BABEL: "babeld", + RD_PBRD: "pbrd", } def __init__(self, tgen, cls, name, **params): diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index b35606df8f..6262082193 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -871,6 +871,7 @@ class Router(Node): "bfdd": 0, "sharpd": 0, "babeld": 0, + "pbrd": 0, } self.daemons_options = {"zebra": ""} self.reportCores = True From b1ab2bd2df30c1393a2cb945c57f9fb82133b69c Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Wed, 27 May 2020 09:55:09 -0400 Subject: [PATCH 11/11] tests: Add a basic test for pbr Just add a basic test for pbr. This code does not actually test installation in the kernel at this point in time. What we do do is make sure pbr is in a sane state after some very basic configuration. Signed-off-by: Donald Sharp --- .../topotests/pbr-topo1/r1/pbr-interface.json | 12 ++ tests/topotests/pbr-topo1/r1/pbr-map.json | 60 ++++++ .../pbr-topo1/r1/pbr-nexthop-groups.json | 58 ++++++ tests/topotests/pbr-topo1/r1/pbrd.conf | 33 ++++ tests/topotests/pbr-topo1/r1/zebra.conf | 11 ++ tests/topotests/pbr-topo1/test_pbr_topo1.py | 180 ++++++++++++++++++ 6 files changed, 354 insertions(+) create mode 100644 tests/topotests/pbr-topo1/r1/pbr-interface.json create mode 100644 tests/topotests/pbr-topo1/r1/pbr-map.json create mode 100644 tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json create mode 100644 tests/topotests/pbr-topo1/r1/pbrd.conf create mode 100644 tests/topotests/pbr-topo1/r1/zebra.conf create mode 100755 tests/topotests/pbr-topo1/test_pbr_topo1.py diff --git a/tests/topotests/pbr-topo1/r1/pbr-interface.json b/tests/topotests/pbr-topo1/r1/pbr-interface.json new file mode 100644 index 0000000000..452b24dcd7 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbr-interface.json @@ -0,0 +1,12 @@ +[ + { + "name":"r1-eth1", + "policy":"EVA", + "valid":true + }, + { + "name":"r1-eth2", + "policy":"DONNA", + "valid":true + } +] diff --git a/tests/topotests/pbr-topo1/r1/pbr-map.json b/tests/topotests/pbr-topo1/r1/pbr-map.json new file mode 100644 index 0000000000..6b9eaa9ceb --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbr-map.json @@ -0,0 +1,60 @@ +[ + { + "name":"DONNA", + "valid":true, + "policies":[ + { + "id":3, + "sequenceNumber":5, + "ruleNumber":304, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "tableId":10002, + "name":"C", + "installed":true, + "installedInternally":1 + }, + "matchSrc":"1.2.0.0\/16", + "matchDst":"3.4.5.0\/24" + } + ] + }, + { + "name":"EVA", + "valid":true, + "policies":[ + { + "id":1, + "sequenceNumber":5, + "ruleNumber":304, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "tableId":10003, + "name":"EVA5", + "installed":true, + "installedInternally":1 + }, + "matchSrc":"4.5.6.7\/32" + }, + { + "id":2, + "sequenceNumber":10, + "ruleNumber":309, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "tableId":10000, + "name":"A", + "installed":true, + "installedInternally":1 + }, + "matchDst":"9.9.9.9\/32" + } + ] + } +] diff --git a/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json b/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json new file mode 100644 index 0000000000..ff85438ad5 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json @@ -0,0 +1,58 @@ +[ + { + "id":10000, + "name":"A", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"192.168.2.2", + "valid":true + }, + { + "nexthop":"192.168.3.2", + "valid":true + }, + { + "nexthop":"192.168.1.2", + "valid":true + } + ] + }, + { + "id":10002, + "name":"C", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"192.168.1.44", + "valid":true + } + ] + }, + { + "id":10001, + "name":"B", + "valid":false, + "installed":false, + "nexthops":[ + { + "nexthop":"192.168.50.1", + "valid":false + } + ] + }, + { + "id":10003, + "name":"EVA5", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"192.168.1.5", + "valid":true + } + ] + } +] diff --git a/tests/topotests/pbr-topo1/r1/pbrd.conf b/tests/topotests/pbr-topo1/r1/pbrd.conf new file mode 100644 index 0000000000..234683f307 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbrd.conf @@ -0,0 +1,33 @@ +nexthop-group A + nexthop 192.168.1.2 + nexthop 192.168.2.2 + nexthop 192.168.3.2 + nexhtop 192.168.4.2 +! +# This one is bogus and should +# never work +nexthop-group B + nexthop 192.168.50.1 +! +nexthop-group C + nexthop 192.168.1.44 +! +pbr-map EVA seq 5 + match src-ip 4.5.6.7/32 + set nexthop 192.168.1.5 +! +pbr-map EVA seq 10 + match dst-ip 9.9.9.9/32 + set nexthop-group A +! +pbr-map DONNA seq 5 + match dst-ip 3.4.5.0/24 + match src-ip 1.2.0.0/16 + set nexthop-group C +! + +int r1-eth1 + pbr-policy EVA +! +int r1-eth2 + pbr-policy DONNA diff --git a/tests/topotests/pbr-topo1/r1/zebra.conf b/tests/topotests/pbr-topo1/r1/zebra.conf new file mode 100644 index 0000000000..f29b146a62 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/zebra.conf @@ -0,0 +1,11 @@ +int r1-eth0 + ip address 192.168.1.1/24 + +int r1-eth1 + ip address 192.168.2.1/24 + +int r1-eth2 + ip address 192.168.3.1/24 + +int r1-eth3 + ip address 192.168.4.1/24 diff --git a/tests/topotests/pbr-topo1/test_pbr_topo1.py b/tests/topotests/pbr-topo1/test_pbr_topo1.py new file mode 100755 index 0000000000..2853165d45 --- /dev/null +++ b/tests/topotests/pbr-topo1/test_pbr_topo1.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python + +# +# test_pbr_topo1.py +# +# Copyright (c) 2020 by +# Cumulus Networks, Inc. +# Donald Sharp +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_pbr_topo1.py: Testing PBR + +""" + +import os +import re +import sys +import pytest +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +class NetworkTopo(Topo): + "PBR Topology 1" + + def build(self, **_opts): + "Build function" + + tgen = get_topogen(self) + + for routern in range(1, 2): + tgen.add_router("r{}".format(routern)) + + # On main router + # First switch is for a dummy interface (for local network) + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["r1"]) + + # Switches for PBR + # switch 2 switch is for connection to PBR router + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["r1"]) + + # switch 4 is stub on remote PBR router + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["r1"]) + + # switch 3 is between PBR routers + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["r1"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(NetworkTopo, module.__name__) + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_PBRD, os.path.join(CWD, "{}/pbrd.conf".format(rname)) + ) + + tgen.start_router() + #gen.mininet_cli() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + topotest.sleep(5, "Waiting for PBR convergence") + + +def test_pbr_data(): + "Test PBR 'show ip eigrp'" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Verify PBR Status + logger.info("Verifying PBR routes") + + router_list = tgen.routers().values() + for router in router_list: + intf_file = "{}/{}/pbr-interface.json".format(CWD, router.name) + + logger.info(intf_file) + # Read expected result from file + expected = json.loads(open(intf_file).read()) + + # Actual output from router + actual = router.vtysh_cmd("show pbr interface json", isjson=True) + + assertmsg = '"show pbr interface" mismatches on {}'.format(router.name) + assert topotest.json_cmp(actual, expected) is None, assertmsg + + map_file = "{}/{}/pbr-map.json".format(CWD, router.name) + logger.info(map_file) + # Read expected result from file + expected = json.loads(open(map_file).read()) + + # Actual output from router + actual = router.vtysh_cmd("show pbr map json", isjson=True) + + assertmsg = '"show pbr map" mismatches on {}'.format(router.name) + assert topotest.json_cmp(actual, expected) is None, assertmsg + + nexthop_file = "{}/{}/pbr-nexthop-groups.json".format(CWD, router.name) + + # Read expected result from file + expected = json.loads(open(nexthop_file).read()) + + # Actual output from router + actual = router.vtysh_cmd("show pbr nexthop-groups json", isjson=True) + + assertmsg = '"show pbr nexthop-groups" mismatches on {}'.format(router.name) + assert topotest.json_cmp(actual, expected) is None, assertmsg + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) +