mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-14 04:26:12 +00:00
Merge pull request #6442 from wesleycoakley/pbrd-showjson
pbrd: Optional JSON output for all `show pbr` directives
This commit is contained in:
commit
b12b5d2097
107
doc/user/pbr.rst
107
doc/user/pbr.rst
@ -56,10 +56,36 @@ listing of ECMP nexthops used to forward packets for when a pbr-map is matched.
|
|||||||
Showing Nexthop Group Information
|
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
|
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 an array of objects which obey the schema below:
|
||||||
|
|
||||||
|
+-----------+----------------------------+---------+
|
||||||
|
| 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:
|
||||||
|
|
||||||
|
+---------+------------------------------+---------+
|
||||||
|
| Key | Description | Type |
|
||||||
|
+=========+==============================+=========+
|
||||||
|
| nexthop | Name of this nexthop | String |
|
||||||
|
+---------+------------------------------+---------+
|
||||||
|
| valid | Is this nexthop well-formed? | Boolean |
|
||||||
|
+---------+------------------------------+---------+
|
||||||
|
|
||||||
.. _pbr-maps:
|
.. _pbr-maps:
|
||||||
|
|
||||||
@ -115,11 +141,68 @@ end destination.
|
|||||||
|
|
||||||
Not supported with NETNS VRF backend.
|
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
|
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
|
give information about the rules unique ID used internally and some extra
|
||||||
debugging information about install state for the nexthop/nexthop group.
|
debugging information about install state for the nexthop/nexthop group.
|
||||||
|
Setting ``json`` will provide the same information in an array of objects
|
||||||
|
which obey the schema below:
|
||||||
|
|
||||||
|
+----------+--------------------------------+---------+
|
||||||
|
| 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
|
||||||
|
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 |
|
||||||
|
+-----------------+-------------------------------------------+---------+
|
||||||
|
| installed | 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 |
|
||||||
|
+=====================+======================================+=========+
|
||||||
|
| 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:
|
.. _pbr-policy:
|
||||||
|
|
||||||
@ -141,6 +224,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
|
even if one is on the master. Each must have the PBR map explicitly added
|
||||||
to the interface.
|
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 |
|
||||||
|
+--------+----------------------------+---------+
|
||||||
|
| valid | Is the map well-formed? | Boolean |
|
||||||
|
+--------+----------------------------+---------+
|
||||||
|
|
||||||
.. _pbr-details:
|
.. _pbr-details:
|
||||||
|
|
||||||
PBR Details
|
PBR Details
|
||||||
|
@ -996,6 +996,60 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
|
|||||||
vty_out(vty, "\n");
|
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, "targetVrf", 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,
|
static void nexthop_group_write_nexthop_internal(struct vty *vty,
|
||||||
struct nexthop_hold *nh)
|
struct nexthop_hold *nh)
|
||||||
{
|
{
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#define __NEXTHOP_GROUP__
|
#define __NEXTHOP_GROUP__
|
||||||
|
|
||||||
#include <vty.h>
|
#include <vty.h>
|
||||||
|
#include "json.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
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_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 */
|
/* Return the number of nexthops in this nhg */
|
||||||
extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg);
|
extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg);
|
||||||
extern uint8_t
|
extern uint8_t
|
||||||
|
@ -1030,8 +1030,22 @@ static void pbr_nht_show_nhg_nexthops(struct hash_bucket *b, void *data)
|
|||||||
nexthop_group_write_nexthop(vty, pnhc->nexthop);
|
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();
|
||||||
|
nexthop_group_json_nexthop(this_hop, pnhc->nexthop);
|
||||||
|
json_object_boolean_add(this_hop, "valid", pnhc->valid);
|
||||||
|
|
||||||
|
json_object_array_add(all_hops, this_hop);
|
||||||
|
}
|
||||||
|
|
||||||
struct pbr_nht_show {
|
struct pbr_nht_show {
|
||||||
struct vty *vty;
|
struct vty *vty;
|
||||||
|
json_object *json;
|
||||||
const char *name;
|
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);
|
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_int_add(this_group, "id", pnhgc->table_id);
|
||||||
|
json_object_string_add(this_group, "name", pnhgc->name);
|
||||||
|
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_array_add(j, this_group);
|
||||||
|
}
|
||||||
|
|
||||||
void pbr_nht_show_nexthop_group(struct vty *vty, const char *name)
|
void pbr_nht_show_nexthop_group(struct vty *vty, const char *name)
|
||||||
{
|
{
|
||||||
struct pbr_nht_show pns;
|
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);
|
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)
|
void pbr_nht_init(void)
|
||||||
{
|
{
|
||||||
pbr_nhg_hash = hash_create_size(
|
pbr_nhg_hash = hash_create_size(
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <lib/nexthop_group.h>
|
#include <lib/nexthop_group.h>
|
||||||
|
|
||||||
#include "pbr_map.h"
|
#include "pbr_map.h"
|
||||||
|
#include "json.h"
|
||||||
|
|
||||||
#define PBR_NHC_NAMELEN PBR_MAP_NAMELEN + 10
|
#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);
|
char *buffer);
|
||||||
|
|
||||||
extern void pbr_nht_show_nexthop_group(struct vty *vty, const char *name);
|
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
|
* When we get a callback from zebra about a nexthop changing
|
||||||
|
159
pbrd/pbr_vty.c
159
pbrd/pbr_vty.c
@ -27,6 +27,7 @@
|
|||||||
#include "nexthop_group.h"
|
#include "nexthop_group.h"
|
||||||
#include "nexthop_group_private.h"
|
#include "nexthop_group_private.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "json.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "pbr.h"
|
#include "pbr.h"
|
||||||
|
|
||||||
@ -590,6 +591,61 @@ 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, *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();
|
||||||
|
|
||||||
|
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_boolean_add(jpbrm, "vrfUnchanged", pbrms->vrf_unchanged);
|
||||||
|
json_object_boolean_add(jpbrm, "installed",
|
||||||
|
pbr_nht_get_installed(nhg_name));
|
||||||
|
json_object_string_add(jpbrm, "installedReason",
|
||||||
|
pbrms->reason ? rbuf : "Valid");
|
||||||
|
|
||||||
|
if (nhg_name) {
|
||||||
|
nexthop_group = json_object_new_object();
|
||||||
|
|
||||||
|
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, "installed",
|
||||||
|
pbr_nht_get_installed(nhg_name));
|
||||||
|
json_object_int_add(nexthop_group, "installedInternally",
|
||||||
|
pbrms->nhs_installed);
|
||||||
|
|
||||||
|
json_object_object_add(jpbrm, "nexthopGroup", nexthop_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pbrms->vrf_lookup)
|
||||||
|
json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm,
|
static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm,
|
||||||
bool detail)
|
bool detail)
|
||||||
{
|
{
|
||||||
@ -603,54 +659,121 @@ static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm,
|
|||||||
vty_show_pbrms(vty, pbrms, detail);
|
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, "policies", jpbrms);
|
||||||
|
}
|
||||||
|
|
||||||
DEFPY (show_pbr_map,
|
DEFPY (show_pbr_map,
|
||||||
show_pbr_map_cmd,
|
show_pbr_map_cmd,
|
||||||
"show pbr map [NAME$name] [detail$detail]",
|
"show pbr map [NAME$name] [detail$detail|json$json]",
|
||||||
SHOW_STR
|
SHOW_STR
|
||||||
PBR_STR
|
PBR_STR
|
||||||
"PBR Map\n"
|
"PBR Map\n"
|
||||||
"PBR Map Name\n"
|
"PBR Map Name\n"
|
||||||
"Detailed information\n")
|
"Detailed information\n"
|
||||||
|
JSON_STR)
|
||||||
{
|
{
|
||||||
struct pbr_map *pbrm;
|
struct pbr_map *pbrm;
|
||||||
|
json_object *j = NULL;
|
||||||
|
|
||||||
|
if (json)
|
||||||
|
j = json_object_new_array();
|
||||||
|
|
||||||
RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
|
RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
|
||||||
|
json_object *this_map = NULL;
|
||||||
if (name && strcmp(name, pbrm->name) != 0)
|
if (name && strcmp(name, pbrm->name) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (j)
|
||||||
|
this_map = json_object_new_object();
|
||||||
|
|
||||||
|
if (this_map) {
|
||||||
|
vty_json_pbr_map(this_map, vty, pbrm);
|
||||||
|
|
||||||
|
json_object_array_add(j, this_map);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
vty_show_pbr_map(vty, pbrm, detail);
|
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;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFPY(show_pbr_nexthop_group,
|
DEFPY(show_pbr_nexthop_group,
|
||||||
show_pbr_nexthop_group_cmd,
|
show_pbr_nexthop_group_cmd,
|
||||||
"show pbr nexthop-groups [WORD$word]",
|
"show pbr nexthop-groups [WORD$word] [json$json]",
|
||||||
SHOW_STR
|
SHOW_STR
|
||||||
PBR_STR
|
PBR_STR
|
||||||
"Nexthop Groups\n"
|
"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_array();
|
||||||
|
|
||||||
|
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;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFPY (show_pbr_interface,
|
DEFPY (show_pbr_interface,
|
||||||
show_pbr_interface_cmd,
|
show_pbr_interface_cmd,
|
||||||
"show pbr interface [NAME$name]",
|
"show pbr interface [NAME$name] [json$json]",
|
||||||
SHOW_STR
|
SHOW_STR
|
||||||
PBR_STR
|
PBR_STR
|
||||||
"PBR Interface\n"
|
"PBR Interface\n"
|
||||||
"PBR Interface Name\n")
|
"PBR Interface Name\n"
|
||||||
|
JSON_STR)
|
||||||
{
|
{
|
||||||
struct interface *ifp;
|
struct interface *ifp;
|
||||||
struct vrf *vrf;
|
struct vrf *vrf;
|
||||||
struct pbr_interface *pbr_ifp;
|
struct pbr_interface *pbr_ifp;
|
||||||
|
json_object *j = NULL;
|
||||||
|
|
||||||
|
if (json)
|
||||||
|
j = json_object_new_array();
|
||||||
|
|
||||||
RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
|
RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
|
||||||
FOR_ALL_INTERFACES(vrf, ifp) {
|
FOR_ALL_INTERFACES(vrf, ifp) {
|
||||||
struct pbr_map *pbrm;
|
struct pbr_map *pbrm;
|
||||||
|
json_object *this_iface = NULL;
|
||||||
|
|
||||||
|
if (j)
|
||||||
|
this_iface = json_object_new_object();
|
||||||
|
|
||||||
if (!ifp->info)
|
if (!ifp->info)
|
||||||
continue;
|
continue;
|
||||||
@ -664,6 +787,21 @@ DEFPY (show_pbr_interface,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
pbrm = pbrm_find(pbr_ifp->mapname);
|
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_array_add(j, this_iface);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
vty_out(vty, " %s(%d) with pbr-policy %s", ifp->name,
|
vty_out(vty, " %s(%d) with pbr-policy %s", ifp->name,
|
||||||
ifp->ifindex, pbr_ifp->mapname);
|
ifp->ifindex, pbr_ifp->mapname);
|
||||||
if (!pbrm)
|
if (!pbrm)
|
||||||
@ -672,6 +810,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;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
tests/topotests/all-protocol-startup/r1/pbrd.conf
Normal file
10
tests/topotests/all-protocol-startup/r1/pbrd.conf
Normal file
@ -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
|
||||||
|
!
|
@ -123,6 +123,7 @@ def setup_module(module):
|
|||||||
net['r%s' % i].loadConf('sharpd')
|
net['r%s' % i].loadConf('sharpd')
|
||||||
net['r%s' % i].loadConf('nhrpd', '%s/r%s/nhrpd.conf' % (thisDir, i))
|
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('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()
|
net['r%s' % i].startRouter()
|
||||||
|
|
||||||
# For debugging after starting Quagga/FRR daemons, uncomment the next line
|
# For debugging after starting Quagga/FRR daemons, uncomment the next line
|
||||||
|
@ -556,6 +556,7 @@ class TopoRouter(TopoGear):
|
|||||||
RD_BFD = 13
|
RD_BFD = 13
|
||||||
RD_SHARP = 14
|
RD_SHARP = 14
|
||||||
RD_BABEL = 15
|
RD_BABEL = 15
|
||||||
|
RD_PBRD = 16
|
||||||
RD = {
|
RD = {
|
||||||
RD_ZEBRA: "zebra",
|
RD_ZEBRA: "zebra",
|
||||||
RD_RIP: "ripd",
|
RD_RIP: "ripd",
|
||||||
@ -572,6 +573,7 @@ class TopoRouter(TopoGear):
|
|||||||
RD_BFD: "bfdd",
|
RD_BFD: "bfdd",
|
||||||
RD_SHARP: "sharpd",
|
RD_SHARP: "sharpd",
|
||||||
RD_BABEL: "babeld",
|
RD_BABEL: "babeld",
|
||||||
|
RD_PBRD: "pbrd",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, tgen, cls, name, **params):
|
def __init__(self, tgen, cls, name, **params):
|
||||||
|
@ -871,6 +871,7 @@ class Router(Node):
|
|||||||
"bfdd": 0,
|
"bfdd": 0,
|
||||||
"sharpd": 0,
|
"sharpd": 0,
|
||||||
"babeld": 0,
|
"babeld": 0,
|
||||||
|
"pbrd": 0,
|
||||||
}
|
}
|
||||||
self.daemons_options = {"zebra": ""}
|
self.daemons_options = {"zebra": ""}
|
||||||
self.reportCores = True
|
self.reportCores = True
|
||||||
|
12
tests/topotests/pbr-topo1/r1/pbr-interface.json
Normal file
12
tests/topotests/pbr-topo1/r1/pbr-interface.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name":"r1-eth1",
|
||||||
|
"policy":"EVA",
|
||||||
|
"valid":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"r1-eth2",
|
||||||
|
"policy":"DONNA",
|
||||||
|
"valid":true
|
||||||
|
}
|
||||||
|
]
|
60
tests/topotests/pbr-topo1/r1/pbr-map.json
Normal file
60
tests/topotests/pbr-topo1/r1/pbr-map.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
58
tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json
Normal file
58
tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json
Normal file
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
33
tests/topotests/pbr-topo1/r1/pbrd.conf
Normal file
33
tests/topotests/pbr-topo1/r1/pbrd.conf
Normal file
@ -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
|
11
tests/topotests/pbr-topo1/r1/zebra.conf
Normal file
11
tests/topotests/pbr-topo1/r1/zebra.conf
Normal file
@ -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
|
180
tests/topotests/pbr-topo1/test_pbr_topo1.py
Executable file
180
tests/topotests/pbr-topo1/test_pbr_topo1.py
Executable file
@ -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))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user