diff options
Diffstat (limited to 'tests')
415 files changed, 56214 insertions, 1619 deletions
diff --git a/tests/.gitignore b/tests/.gitignore index 5e809a81e6..ca20b0ecac 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -47,7 +47,9 @@ /lib/test_ttable /lib/test_typelist /lib/test_versioncmp +/lib/test_xref /lib/test_zlog /lib/test_zmq /ospf6d/test_lsdb /ospf6d/test_lsdb_clippy.c +/zebra/test_lm_plugin
\ No newline at end of file diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c index 439891b559..936ffaaad5 100644 --- a/tests/bgpd/test_aspath.c +++ b/tests/bgpd/test_aspath.c @@ -1265,7 +1265,8 @@ int main(void) { int i = 0; qobj_init(); - bgp_master_init(thread_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE); + bgp_master_init(thread_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE, + list_new()); master = bm->master; bgp_option_set(BGP_OPT_NO_LISTEN); bgp_attr_init(); diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c index 1b3f90434b..153b83897d 100644 --- a/tests/bgpd/test_capability.c +++ b/tests/bgpd/test_capability.c @@ -912,7 +912,7 @@ int main(void) qobj_init(); master = thread_master_create(NULL); - bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); + bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new()); vrf_init(NULL, NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index 7fabaad7fa..f510760913 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -1086,7 +1086,7 @@ int main(void) cmd_init(0); bgp_vty_init(); master = thread_master_create("test mp attr"); - bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); + bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new()); vrf_init(NULL, NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); bgp_attr_init(); diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c index 520c460f15..92efd4c3d6 100644 --- a/tests/bgpd/test_mpath.c +++ b/tests/bgpd/test_mpath.c @@ -393,7 +393,7 @@ static int global_test_init(void) qobj_init(); master = thread_master_create(NULL); zclient = zclient_new(master, &zclient_options_default); - bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); + bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new()); vrf_init(NULL, NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c index d2c093fbea..db5918745d 100644 --- a/tests/bgpd/test_packet.c +++ b/tests/bgpd/test_packet.c @@ -59,7 +59,7 @@ int main(int argc, char *argv[]) qobj_init(); bgp_attr_init(); master = thread_master_create(NULL); - bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); + bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new()); vrf_init(NULL, NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c index 0a15886c10..123d97bc97 100644 --- a/tests/bgpd/test_peer_attr.c +++ b/tests/bgpd/test_peer_attr.c @@ -1399,7 +1399,7 @@ static void bgp_startup(void) master = thread_master_create(NULL); yang_init(true); nb_init(master, bgpd_yang_modules, array_size(bgpd_yang_modules), false); - bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); + bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new()); bgp_option_set(BGP_OPT_NO_LISTEN); vrf_init(NULL, NULL, NULL, NULL, NULL); frr_pthread_init(); diff --git a/tests/isisd/test_common.c b/tests/isisd/test_common.c index 5fa604c749..5b2028ffd4 100644 --- a/tests/isisd/test_common.c +++ b/tests/isisd/test_common.c @@ -69,6 +69,24 @@ test_find_adjacency(const struct isis_test_node *tnode, const char *hostname) return NULL; } +mpls_label_t test_topology_node_ldp_label(const struct isis_topology *topology, + struct in_addr router_id) +{ + for (size_t i = 0; topology->nodes[i].hostname[0]; i++) { + const struct isis_test_node *tnode = &topology->nodes[i]; + struct in_addr node_router_id; + + if (!tnode->router_id) + continue; + + (void)inet_pton(AF_INET, tnode->router_id, &node_router_id); + if (IPV4_ADDR_SAME(&router_id, &node_router_id)) + return (50000 + (i + 1) * 100); + } + + return MPLS_INVALID_LABEL; +} + static struct isis_lsp *lsp_add(struct lspdb_head *lspdb, struct isis_area *area, int level, const uint8_t *sysid, uint8_t pseudonode_id) diff --git a/tests/isisd/test_common.h b/tests/isisd/test_common.h index 6fd0d3813e..3359a893ac 100644 --- a/tests/isisd/test_common.h +++ b/tests/isisd/test_common.h @@ -70,6 +70,9 @@ test_topology_find_node(const struct isis_topology *topology, const char *hostname, uint8_t pseudonode_id); extern const struct isis_topology * test_topology_find(struct isis_topology *test_topologies, uint16_t number); +extern mpls_label_t +test_topology_node_ldp_label(const struct isis_topology *topology, + struct in_addr router_id); extern int test_topology_load(const struct isis_topology *topology, struct isis_area *area, struct lspdb_head lspdb[]); diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c index 36ef93669b..e06944a037 100644 --- a/tests/isisd/test_isis_spf.c +++ b/tests/isisd/test_isis_spf.c @@ -31,6 +31,7 @@ #include "isisd/isisd.h" #include "isisd/isis_dynhn.h" #include "isisd/isis_misc.h" +#include "isisd/isis_route.h" #include "isisd/isis_spf.h" #include "isisd/isis_spf_private.h" @@ -40,6 +41,7 @@ enum test_type { TEST_SPF = 1, TEST_REVERSE_SPF, TEST_LFA, + TEST_RLFA, TEST_TI_LFA, }; @@ -105,6 +107,86 @@ static void test_run_lfa(struct vty *vty, const struct isis_topology *topology, isis_spftree_del(spftree_self); } +static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology, + const struct isis_test_node *root, + struct isis_area *area, struct lspdb_head *lspdb, + int level, int tree, + struct lfa_protected_resource *protected_resource) +{ + struct isis_spftree *spftree_self; + struct isis_spftree *spftree_reverse; + struct isis_spftree *spftree_pc; + struct isis_spf_node *spf_node, *node; + struct rlfa *rlfa; + uint8_t flags; + + /* Run forward SPF in the root node. */ + flags = F_SPFTREE_NO_ADJACENCIES; + spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree, + SPF_TYPE_FORWARD, flags); + isis_run_spf(spftree_self); + + /* Run reverse SPF in the root node. */ + spftree_reverse = isis_spf_reverse_run(spftree_self); + + /* Run forward SPF on all adjacent routers. */ + isis_spf_run_neighbors(spftree_self); + + /* Compute the local LFA repair paths. */ + isis_lfa_compute(area, NULL, spftree_self, protected_resource); + + /* Compute the remote LFA repair paths. */ + spftree_pc = isis_rlfa_compute(area, spftree_self, spftree_reverse, 0, + protected_resource); + + /* Print the extended P-space and Q-space. */ + vty_out(vty, "P-space (self):\n"); + RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.p_space) + vty_out(vty, " %s\n", print_sys_hostname(node->sysid)); + vty_out(vty, "\n"); + RB_FOREACH (spf_node, isis_spf_nodes, &spftree_self->adj_nodes) { + if (RB_EMPTY(isis_spf_nodes, &spf_node->lfa.p_space)) + continue; + vty_out(vty, "P-space (%s):\n", + print_sys_hostname(spf_node->sysid)); + RB_FOREACH (node, isis_spf_nodes, &spf_node->lfa.p_space) + vty_out(vty, " %s\n", print_sys_hostname(node->sysid)); + vty_out(vty, "\n"); + } + vty_out(vty, "Q-space:\n"); + RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.q_space) + vty_out(vty, " %s\n", print_sys_hostname(node->sysid)); + vty_out(vty, "\n"); + + /* Print the post-convergence SPT. */ + isis_print_spftree(vty, spftree_pc); + + /* + * Activate the computed RLFAs (if any) using artificial LDP labels for + * the PQ nodes. + */ + frr_each_safe (rlfa_tree, &spftree_self->lfa.remote.rlfas, rlfa) { + struct zapi_rlfa_response response = {}; + + response.pq_label = test_topology_node_ldp_label( + topology, rlfa->pq_address); + assert(response.pq_label != MPLS_INVALID_LABEL); + isis_rlfa_activate(spftree_self, rlfa, &response); + } + + /* Print the SPT and the corresponding main/backup routing tables. */ + isis_print_spftree(vty, spftree_self); + vty_out(vty, "Main:\n"); + isis_print_routes(vty, spftree_self, false, false); + vty_out(vty, "Backup:\n"); + isis_print_routes(vty, spftree_self, false, true); + + /* Cleanup everything. */ + isis_spftree_del(spftree_self); + isis_spftree_del(spftree_reverse); + isis_spftree_del(spftree_pc); +} + static void test_run_ti_lfa(struct vty *vty, const struct isis_topology *topology, const struct isis_test_node *root, @@ -242,6 +324,11 @@ static int test_run(struct vty *vty, const struct isis_topology *topology, &area->lspdb[level - 1], level, tree, &protected_resource); break; + case TEST_RLFA: + test_run_rlfa(vty, topology, root, area, + &area->lspdb[level - 1], level, + tree, &protected_resource); + break; case TEST_TI_LFA: test_run_ti_lfa(vty, topology, root, area, &area->lspdb[level - 1], level, @@ -266,6 +353,7 @@ DEFUN(test_isis, test_isis_cmd, spf\ |reverse-spf\ |lfa system-id WORD [pseudonode-id <1-255>]\ + |remote-lfa system-id WORD [pseudonode-id <1-255>]\ |ti-lfa system-id WORD [pseudonode-id <1-255>] [node-protection]\ >\ [display-lspdb] [<ipv4-only|ipv6-only>] [<level-1-only|level-2-only>]", @@ -282,6 +370,11 @@ DEFUN(test_isis, test_isis_cmd, "System ID\n" "Pseudonode-ID\n" "Pseudonode-ID\n" + "Remote LFA\n" + "System ID\n" + "System ID\n" + "Pseudonode-ID\n" + "Pseudonode-ID\n" "Topology Independent LFA\n" "System ID\n" "System ID\n" @@ -335,6 +428,14 @@ DEFUN(test_isis, test_isis_cmd, fail_pseudonode_id = strtoul(argv[idx + 1]->arg, NULL, 10); protection_type = LFA_LINK_PROTECTION; + } else if (argv_find(argv, argc, "remote-lfa", &idx)) { + test_type = TEST_RLFA; + + fail_sysid_str = argv[idx + 2]->arg; + if (argv_find(argv, argc, "pseudonode-id", &idx)) + fail_pseudonode_id = + strtoul(argv[idx + 1]->arg, NULL, 10); + protection_type = LFA_LINK_PROTECTION; } else if (argv_find(argv, argc, "ti-lfa", &idx)) { test_type = TEST_TI_LFA; diff --git a/tests/isisd/test_isis_spf.in b/tests/isisd/test_isis_spf.in index 93e18124e6..f8f65ffdf7 100644 --- a/tests/isisd/test_isis_spf.in +++ b/tests/isisd/test_isis_spf.in @@ -31,6 +31,18 @@ test isis topology 14 root rt1 lfa system-id rt1 pseudonode-id 1 test isis topology 14 root rt1 lfa system-id rt2 test isis topology 14 root rt5 lfa system-id rt4 +test isis topology 1 root rt1 remote-lfa system-id rt2 +test isis topology 2 root rt5 remote-lfa system-id rt1 pseudonode-id 1 +test isis topology 3 root rt5 remote-lfa system-id rt4 ipv4-only +test isis topology 3 root rt5 remote-lfa system-id rt3 ipv4-only +test isis topology 5 root rt1 remote-lfa system-id rt2 ipv4-only +test isis topology 6 root rt4 remote-lfa system-id rt3 ipv4-only +test isis topology 7 root rt11 remote-lfa system-id rt8 ipv4-only +test isis topology 7 root rt6 remote-lfa system-id rt5 ipv4-only +test isis topology 8 root rt2 remote-lfa system-id rt5 ipv4-only +test isis topology 11 root rt2 remote-lfa system-id rt4 +test isis topology 13 root rt1 remote-lfa system-id rt3 ipv4-only + test isis topology 1 root rt1 ti-lfa system-id rt2 test isis topology 2 root rt1 ti-lfa system-id rt3 test isis topology 2 root rt1 ti-lfa system-id rt1 pseudonode-id 1 diff --git a/tests/isisd/test_isis_spf.refout b/tests/isisd/test_isis_spf.refout index dced6fb103..024f7256e0 100644 --- a/tests/isisd/test_isis_spf.refout +++ b/tests/isisd/test_isis_spf.refout @@ -1807,6 +1807,1227 @@ IS-IS L1 IPv6 routing table: 2001:db8::4/128 60 - rt3 -
test#
+test# test isis topology 1 root rt1 remote-lfa system-id rt2
+P-space (self):
+ rt3
+ rt5
+
+P-space (rt3):
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt2
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt5(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt4 TE-IS 40 rt3 - rt6(4)
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)
+rt2 TE-IS 50 rt3 - rt4(4)
+10.0.255.4/32 IP TE 50 rt3 - rt4(4)
+10.0.255.2/32 IP TE 60 rt3 - rt2(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+ rt3 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ - rt3 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.2/32 60 - rt3 50600/16020
+ 10.0.255.4/32 50 - rt3 50600/16040
+
+P-space (self):
+ rt3
+ rt5
+
+P-space (rt3):
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt2
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt5(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+rt4 TE-IS 40 rt3 - rt6(4)
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)
+rt2 TE-IS 50 rt3 - rt4(4)
+2001:db8::4/128 IP6 internal 50 rt3 - rt4(4)
+2001:db8::2/128 IP6 internal 60 rt3 - rt2(4)
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt2 - rt6(4)
+ rt3 -
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 implicit-null
+ 2001:db8::3/128 20 - rt3 implicit-null
+ 2001:db8::4/128 30 - rt2 16041
+ 2001:db8::5/128 30 - rt3 16051
+ 2001:db8::6/128 40 - rt2 16061
+ - rt3 16061
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 2001:db8::2/128 60 - rt3 50600/16021
+ 2001:db8::4/128 50 - rt3 50600/16041
+
+test# test isis topology 2 root rt5 remote-lfa system-id rt1 pseudonode-id 1
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+
+P-space (rt6):
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt1 pseudo_TE-IS 30 rt6 - rt4(4)
+rt1 TE-IS 30 rt6 - rt1(2)
+10.0.255.4/32 IP TE 30 rt6 - rt4(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+10.0.255.1/32 IP TE 40 rt6 - rt1(4)
+rt2 TE-IS 45 rt6 - rt1(4)
+10.0.255.3/32 IP TE 50 rt3 - rt3(4)
+10.0.255.2/32 IP TE 55 rt6 - rt2(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt1 TE-IS 10 rt1 - rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt4 - rt4(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+10.0.255.2/32 IP TE 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+ rt1 - rt1(4)
+10.0.255.3/32 IP TE 50 rt3 - rt3(4)
+ rt1 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 35 - rt1 16020
+ 10.0.255.3/32 50 - rt3 implicit-null
+ - rt1 implicit-null
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 0 - - -
+ 10.0.255.6/32 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.1/32 40 - rt6 50400/16010
+ 10.0.255.2/32 55 - rt6 50400/16020
+ 10.0.255.4/32 30 - rt6 50400/16040
+
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+
+P-space (rt6):
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+2001:db8::5/128 IP6 internal 0 rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+rt1 pseudo_TE-IS 30 rt6 - rt4(4)
+rt1 TE-IS 30 rt6 - rt1(2)
+2001:db8::4/128 IP6 internal 30 rt6 - rt4(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+2001:db8::1/128 IP6 internal 40 rt6 - rt1(4)
+rt2 TE-IS 45 rt6 - rt1(4)
+2001:db8::3/128 IP6 internal 50 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 55 rt6 - rt2(4)
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+2001:db8::5/128 IP6 internal 0 rt5(4)
+rt1 TE-IS 10 rt1 - rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt4 - rt4(4)
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+2001:db8::2/128 IP6 internal 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+ rt1 - rt1(4)
+2001:db8::3/128 IP6 internal 50 rt3 - rt3(4)
+ rt1 -
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 20 - rt1 implicit-null
+ 2001:db8::2/128 35 - rt1 16021
+ 2001:db8::3/128 50 - rt3 implicit-null
+ - rt1 implicit-null
+ 2001:db8::4/128 20 - rt4 implicit-null
+ 2001:db8::5/128 0 - - -
+ 2001:db8::6/128 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 2001:db8::1/128 40 - rt6 50400/16011
+ 2001:db8::2/128 55 - rt6 50400/16021
+ 2001:db8::4/128 30 - rt6 50400/16041
+
+test# test isis topology 3 root rt5 remote-lfa system-id rt4 ipv4-only
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+P-space (rt6):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt3 - rt5(4)
+rt2 TE-IS 30 rt6 - rt4(4)
+10.0.255.4/32 IP TE 30 rt6 - rt4(4)
+rt1 TE-IS 40 rt3 - rt3(4)
+ rt6 - rt2(4)
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+10.0.255.2/32 IP TE 40 rt6 - rt2(4)
+10.0.255.1/32 IP TE 50 rt3 - rt1(4)
+ rt6 -
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt2 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt3 - rt5(4)
+ rt4 - rt2(4)
+rt1 TE-IS 30 rt4 - rt2(4)
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+ rt4 -
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 40 - rt4 16010
+ 10.0.255.2/32 30 - rt4 16020
+ 10.0.255.3/32 40 - rt3 implicit-null
+ - rt4 implicit-null
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 0 - - -
+ 10.0.255.6/32 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 40 - rt3 16010
+ - rt6 16010
+ 10.0.255.2/32 30 - rt3 16020
+ - rt6 16020
+ 10.0.255.4/32 20 - rt3 16040
+ - rt6 16040
+
+test# test isis topology 3 root rt5 remote-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt1
+ rt2
+ rt4
+ rt6
+
+P-space (rt4):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+P-space (rt6):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt2 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt1 TE-IS 30 rt4 - rt2(4)
+rt3 TE-IS 30 rt4 - rt2(4)
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)
+10.0.255.3/32 IP TE 40 rt4 - rt3(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt2 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt3 - rt5(4)
+ rt4 - rt2(4)
+rt1 TE-IS 30 rt4 - rt2(4)
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+ rt4 -
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 40 - rt4 16010
+ 10.0.255.2/32 30 - rt4 16020
+ 10.0.255.3/32 40 - rt3 implicit-null
+ - rt4 implicit-null
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 0 - - -
+ 10.0.255.6/32 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+test# test isis topology 5 root rt1 remote-lfa system-id rt2 ipv4-only
+P-space (self):
+ rt3
+ rt5
+ rt7
+
+P-space (rt3):
+ rt3
+ rt5
+ rt7
+ rt8
+
+Q-space:
+ rt2
+ rt4
+ rt6
+ rt8
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt3 - rt7(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+rt6 TE-IS 50 rt3 - rt8(4)
+10.0.255.8/32 IP TE 50 rt3 - rt8(4)
+rt4 TE-IS 60 rt3 - rt6(4)
+10.0.255.6/32 IP TE 60 rt3 - rt6(4)
+rt2 TE-IS 70 rt3 - rt4(4)
+10.0.255.4/32 IP TE 70 rt3 - rt4(4)
+10.0.255.2/32 IP TE 80 rt3 - rt2(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt2 - rt6(4)
+ rt3 - rt7(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+ rt3 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ 10.0.255.7/32 40 - rt3 16070
+ 10.0.255.8/32 50 - rt2 16080
+ - rt3 16080
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.2/32 80 - rt3 50800/16020
+ 10.0.255.4/32 70 - rt3 50800/16040
+ 10.0.255.6/32 60 - rt3 50800/16060
+
+test# test isis topology 6 root rt4 remote-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt2
+ rt5
+ rt6
+ rt7
+ rt8
+
+P-space (rt2):
+ rt1
+ rt2
+
+P-space (rt6):
+ rt5
+ rt6
+ rt7
+ rt8
+
+Q-space:
+ rt1
+ rt3
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt2 TE-IS 10 rt2 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt6 - rt6(4)
+rt8 TE-IS 20 rt6 - rt6(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt2 - rt1(4)
+rt7 TE-IS 30 rt6 - rt5(4)
+ rt8(4)
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)
+10.0.255.5/32 IP TE 30 rt6 - rt5(4)
+10.0.255.8/32 IP TE 30 rt6 - rt8(4)
+10.0.255.3/32 IP TE 40 rt2 - rt3(4)
+10.0.255.7/32 IP TE 40 rt6 - rt7(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt2 TE-IS 10 rt2 - rt4(4)
+rt3 TE-IS 10 rt3 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 TE-IS 20 rt2 - rt2(4)
+ rt3 - rt3(4)
+rt5 TE-IS 20 rt6 - rt6(4)
+rt8 TE-IS 20 rt6 - rt6(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt7 TE-IS 30 rt6 - rt5(4)
+ rt8(4)
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)
+ rt3 -
+10.0.255.5/32 IP TE 30 rt6 - rt5(4)
+10.0.255.8/32 IP TE 30 rt6 - rt8(4)
+10.0.255.7/32 IP TE 40 rt6 - rt7(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 30 - rt2 16010
+ - rt3 16010
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 0 - - -
+ 10.0.255.5/32 30 - rt6 16050
+ 10.0.255.6/32 20 - rt6 implicit-null
+ 10.0.255.7/32 40 - rt6 16070
+ 10.0.255.8/32 30 - rt6 16080
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.3/32 40 - rt2 50100/16030
+
+test# test isis topology 7 root rt11 remote-lfa system-id rt8 ipv4-only
+P-space (self):
+ rt10
+ rt12
+
+P-space (rt10):
+ rt1
+ rt4
+ rt7
+ rt10
+
+P-space (rt12):
+ rt9
+ rt12
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt5
+ rt6
+ rt7
+ rt8
+ rt9
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt11
+10.0.255.11/32 IP internal 0 rt11(4)
+rt10 TE-IS 10 rt10 - rt11(4)
+rt12 TE-IS 10 rt12 - rt11(4)
+rt9 TE-IS 20 rt12 - rt12(4)
+10.0.255.10/32 IP TE 20 rt10 - rt10(4)
+10.0.255.12/32 IP TE 20 rt12 - rt12(4)
+rt7 TE-IS 30 rt10 - rt10(4)
+rt8 TE-IS 30 rt12 - rt9(4)
+10.0.255.9/32 IP TE 30 rt12 - rt9(4)
+rt4 TE-IS 40 rt10 - rt7(4)
+rt5 TE-IS 40 rt12 - rt8(4)
+10.0.255.7/32 IP TE 40 rt10 - rt7(4)
+10.0.255.8/32 IP TE 40 rt12 - rt8(4)
+rt6 TE-IS 50 rt12 - rt9(4)
+ rt5(4)
+rt1 TE-IS 50 rt10 - rt4(4)
+rt2 TE-IS 50 rt12 - rt5(4)
+10.0.255.4/32 IP TE 50 rt10 - rt4(4)
+10.0.255.5/32 IP TE 50 rt12 - rt5(4)
+rt3 TE-IS 60 rt12 - rt6(4)
+ rt2(4)
+10.0.255.6/32 IP TE 60 rt12 - rt6(4)
+10.0.255.1/32 IP TE 60 rt10 - rt1(4)
+10.0.255.2/32 IP TE 60 rt12 - rt2(4)
+10.0.255.3/32 IP TE 70 rt12 - rt3(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt11
+10.0.255.11/32 IP internal 0 rt11(4)
+rt8 TE-IS 10 rt8 - rt11(4)
+rt10 TE-IS 10 rt10 - rt11(4)
+rt12 TE-IS 10 rt12 - rt11(4)
+rt5 TE-IS 20 rt8 - rt8(4)
+rt7 TE-IS 20 rt8 - rt8(4)
+rt9 TE-IS 20 rt8 - rt8(4)
+ rt12 - rt12(4)
+10.0.255.8/32 IP TE 20 rt8 - rt8(4)
+10.0.255.10/32 IP TE 20 rt10 - rt10(4)
+10.0.255.12/32 IP TE 20 rt12 - rt12(4)
+rt2 TE-IS 30 rt8 - rt5(4)
+rt4 TE-IS 30 rt8 - rt5(4)
+ rt7(4)
+rt6 TE-IS 30 rt8 - rt5(4)
+10.0.255.5/32 IP TE 30 rt8 - rt5(4)
+10.0.255.7/32 IP TE 30 rt8 - rt7(4)
+10.0.255.9/32 IP TE 30 rt8 - rt9(4)
+ rt12 -
+rt3 TE-IS 40 rt8 - rt2(4)
+ rt6(4)
+rt1 TE-IS 40 rt8 - rt4(4)
+10.0.255.2/32 IP TE 40 rt8 - rt2(4)
+10.0.255.4/32 IP TE 40 rt8 - rt4(4)
+10.0.255.6/32 IP TE 40 rt8 - rt6(4)
+10.0.255.3/32 IP TE 50 rt8 - rt3(4)
+10.0.255.1/32 IP TE 50 rt8 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 50 - rt8 16010
+ 10.0.255.2/32 40 - rt8 16020
+ 10.0.255.3/32 50 - rt8 16030
+ 10.0.255.4/32 40 - rt8 16040
+ 10.0.255.5/32 30 - rt8 16050
+ 10.0.255.6/32 40 - rt8 16060
+ 10.0.255.7/32 30 - rt8 16070
+ 10.0.255.8/32 20 - rt8 implicit-null
+ 10.0.255.9/32 30 - rt8 16090
+ - rt12 16090
+ 10.0.255.10/32 20 - rt10 implicit-null
+ 10.0.255.11/32 0 - - -
+ 10.0.255.12/32 20 - rt12 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.1/32 50 - rt10 16010
+ 10.0.255.2/32 60 - rt12 50900/16020
+ 10.0.255.3/32 70 - rt12 50900/16030
+ 10.0.255.4/32 40 - rt10 16040
+ 10.0.255.5/32 50 - rt12 50900/16050
+ 10.0.255.6/32 60 - rt12 50900/16060
+ 10.0.255.7/32 30 - rt10 16070
+ 10.0.255.8/32 40 - rt12 50900/16080
+
+test# test isis topology 7 root rt6 remote-lfa system-id rt5 ipv4-only
+P-space (self):
+ rt3
+
+P-space (rt3):
+ rt2
+ rt3
+
+P-space (rt9):
+ rt1
+ rt2
+ rt4
+ rt5
+ rt7
+ rt8
+ rt9
+ rt10
+ rt11
+ rt12
+
+Q-space:
+ rt1
+ rt2
+ rt4
+ rt5
+ rt7
+ rt8
+ rt9
+ rt10
+ rt11
+ rt12
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt6
+10.0.255.6/32 IP internal 0 rt6(4)
+rt3 TE-IS 10 rt3 - rt6(4)
+rt2 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt9 TE-IS 30 rt9 - rt6(4)
+rt5 TE-IS 30 rt3 - rt2(4)
+10.0.255.2/32 IP TE 30 rt3 - rt2(4)
+rt8 TE-IS 40 rt9 - rt9(4)
+ rt3 - rt5(4)
+rt12 TE-IS 40 rt9 - rt9(4)
+rt4 TE-IS 40 rt3 - rt5(4)
+10.0.255.9/32 IP TE 40 rt9 - rt9(4)
+10.0.255.5/32 IP TE 40 rt3 - rt5(4)
+rt7 TE-IS 50 rt9 - rt8(4)
+ rt3 - rt4(4)
+rt11 TE-IS 50 rt9 - rt8(4)
+ rt3 - rt12(4)
+rt1 TE-IS 50 rt3 - rt4(4)
+10.0.255.8/32 IP TE 50 rt9 - rt8(4)
+ rt3 -
+10.0.255.12/32 IP TE 50 rt9 - rt12(4)
+10.0.255.4/32 IP TE 50 rt3 - rt4(4)
+rt10 TE-IS 60 rt9 - rt11(4)
+ rt3 -
+10.0.255.7/32 IP TE 60 rt9 - rt7(4)
+ rt3 -
+10.0.255.11/32 IP TE 60 rt9 - rt11(4)
+ rt3 -
+10.0.255.1/32 IP TE 60 rt3 - rt1(4)
+10.0.255.10/32 IP TE 70 rt9 - rt10(4)
+ rt3 -
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt6
+10.0.255.6/32 IP internal 0 rt6(4)
+rt3 TE-IS 10 rt3 - rt6(4)
+rt5 TE-IS 10 rt5 - rt6(4)
+rt2 TE-IS 20 rt3 - rt3(4)
+ rt5 - rt5(4)
+rt4 TE-IS 20 rt5 - rt5(4)
+rt8 TE-IS 20 rt5 - rt5(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+rt9 TE-IS 30 rt9 - rt6(4)
+ rt5 - rt8(4)
+rt1 TE-IS 30 rt5 - rt4(4)
+rt7 TE-IS 30 rt5 - rt4(4)
+ rt8(4)
+rt11 TE-IS 30 rt5 - rt8(4)
+10.0.255.2/32 IP TE 30 rt3 - rt2(4)
+ rt5 -
+10.0.255.4/32 IP TE 30 rt5 - rt4(4)
+10.0.255.8/32 IP TE 30 rt5 - rt8(4)
+rt12 TE-IS 40 rt9 - rt9(4)
+ rt5 - rt11(4)
+rt10 TE-IS 40 rt5 - rt11(4)
+10.0.255.9/32 IP TE 40 rt9 - rt9(4)
+ rt5 -
+10.0.255.1/32 IP TE 40 rt5 - rt1(4)
+10.0.255.7/32 IP TE 40 rt5 - rt7(4)
+10.0.255.11/32 IP TE 40 rt5 - rt11(4)
+10.0.255.12/32 IP TE 50 rt9 - rt12(4)
+ rt5 -
+10.0.255.10/32 IP TE 50 rt5 - rt10(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 40 - rt5 16010
+ 10.0.255.2/32 30 - rt3 16020
+ - rt5 16020
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt5 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 0 - - -
+ 10.0.255.7/32 40 - rt5 16070
+ 10.0.255.8/32 30 - rt5 16080
+ 10.0.255.9/32 40 - rt9 implicit-null
+ - rt5 implicit-null
+ 10.0.255.10/32 50 - rt5 16100
+ 10.0.255.11/32 40 - rt5 16110
+ 10.0.255.12/32 50 - rt9 16120
+ - rt5 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------
+ 10.0.255.1/32 70 - rt9 16010
+ 10.0.255.4/32 60 - rt9 16040
+ 10.0.255.5/32 50 - rt9 16050
+ 10.0.255.7/32 50 - rt9 16070
+ 10.0.255.8/32 40 - rt9 16080
+ 10.0.255.10/32 60 - rt9 16100
+ 10.0.255.11/32 50 - rt9 16110
+
+test# test isis topology 8 root rt2 remote-lfa system-id rt5 ipv4-only
+P-space (self):
+ rt1
+ rt3
+ rt4
+ rt7
+ rt10
+
+P-space (rt1):
+ rt1
+ rt4
+ rt7
+ rt10
+
+P-space (rt3):
+ rt3
+ rt6
+
+Q-space:
+ rt5
+ rt6
+ rt8
+ rt9
+ rt11
+ rt12
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt1 TE-IS 10 rt1 - rt2(4)
+rt3 TE-IS 10 rt3 - rt2(4)
+rt4 TE-IS 20 rt1 - rt1(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt1 - rt4(4)
+rt5 TE-IS 30 rt3 - rt6(4)
+10.0.255.4/32 IP TE 30 rt1 - rt4(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+rt10 TE-IS 40 rt1 - rt7(4)
+rt8 TE-IS 40 rt3 - rt5(4)
+10.0.255.7/32 IP TE 40 rt1 - rt7(4)
+10.0.255.5/32 IP TE 40 rt3 - rt5(4)
+rt9 TE-IS 50 rt3 - rt8(4)
+rt11 TE-IS 50 rt3 - rt8(4)
+10.0.255.10/32 IP TE 50 rt1 - rt10(4)
+10.0.255.8/32 IP TE 50 rt3 - rt8(4)
+rt12 TE-IS 60 rt3 - rt9(4)
+ rt11(4)
+10.0.255.9/32 IP TE 60 rt3 - rt9(4)
+10.0.255.11/32 IP TE 60 rt3 - rt11(4)
+10.0.255.12/32 IP TE 70 rt3 - rt12(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt1 TE-IS 10 rt1 - rt2(4)
+rt3 TE-IS 10 rt3 - rt2(4)
+rt5 TE-IS 10 rt5 - rt2(4)
+rt4 TE-IS 20 rt1 - rt1(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+ rt5 - rt5(4)
+rt8 TE-IS 20 rt5 - rt5(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+rt7 TE-IS 30 rt1 - rt4(4)
+rt9 TE-IS 30 rt5 - rt8(4)
+rt11 TE-IS 30 rt5 - rt8(4)
+10.0.255.4/32 IP TE 30 rt1 - rt4(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+ rt5 -
+10.0.255.8/32 IP TE 30 rt5 - rt8(4)
+rt10 TE-IS 40 rt1 - rt7(4)
+rt12 TE-IS 40 rt5 - rt9(4)
+ rt11(4)
+10.0.255.7/32 IP TE 40 rt1 - rt7(4)
+10.0.255.9/32 IP TE 40 rt5 - rt9(4)
+10.0.255.11/32 IP TE 40 rt5 - rt11(4)
+10.0.255.10/32 IP TE 50 rt1 - rt10(4)
+10.0.255.12/32 IP TE 50 rt5 - rt12(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 0 - - -
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt1 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 30 - rt3 16060
+ - rt5 16060
+ 10.0.255.7/32 40 - rt1 16070
+ 10.0.255.8/32 30 - rt5 16080
+ 10.0.255.9/32 40 - rt5 16090
+ 10.0.255.10/32 50 - rt1 16100
+ 10.0.255.11/32 40 - rt5 16110
+ 10.0.255.12/32 50 - rt5 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ---------------------------------------------------------
+ 10.0.255.5/32 40 - rt3 50600/16050
+ 10.0.255.8/32 50 - rt3 50600/16080
+ 10.0.255.9/32 60 - rt3 50600/16090
+ 10.0.255.11/32 60 - rt3 50600/16110
+ 10.0.255.12/32 70 - rt3 50600/16120
+
+test# test isis topology 11 root rt2 remote-lfa system-id rt4
+P-space (self):
+
+P-space (rt1):
+ rt1
+ rt3
+ rt5
+
+P-space (rt3):
+ rt1
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt1
+ rt3
+ rt4
+ rt5
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt1 TE-IS 50 rt1 - rt2(4)
+rt3 TE-IS 50 rt3 - rt2(4)
+rt2
+rt5 TE-IS 60 rt3 - rt3(4)
+10.0.255.1/32 IP TE 60 rt1 - rt1(4)
+10.0.255.3/32 IP TE 60 rt3 - rt3(4)
+rt4 TE-IS 70 rt3 - rt5(4)
+rt6 TE-IS 70 rt3 - rt5(4)
+10.0.255.5/32 IP TE 70 rt3 - rt5(4)
+10.0.255.4/32 IP TE 80 rt3 - rt4(4)
+10.0.255.6/32 IP TE 80 rt3 - rt6(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt4 TE-IS 10 rt4 - rt2(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+rt6 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+rt3 TE-IS 30 rt4 - rt5(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+10.0.255.6/32 IP TE 30 rt4 - rt6(4)
+rt2
+rt1 TE-IS 40 rt4 - rt2(2)
+10.0.255.3/32 IP TE 40 rt4 - rt3(4)
+10.0.255.1/32 IP TE 50 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 50 - rt4 16010
+ 10.0.255.2/32 0 - - -
+ 10.0.255.3/32 40 - rt4 16030
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 30 - rt4 16050
+ 10.0.255.6/32 30 - rt4 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 50 - rt1 implicit-null
+ - rt3 16010
+ 10.0.255.3/32 50 - rt1 16030
+ - rt3 implicit-null
+ 10.0.255.4/32 80 - rt3 50500/16040
+ 10.0.255.5/32 60 - rt1 16050
+ - rt3 16050
+ 10.0.255.6/32 70 - rt3 16060
+
+P-space (self):
+
+P-space (rt1):
+ rt1
+ rt3
+ rt5
+
+P-space (rt3):
+ rt1
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt1
+ rt3
+ rt4
+ rt5
+ rt6
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+2001:db8::2/128 IP6 internal 0 rt2(4)
+rt1 TE-IS 50 rt1 - rt2(4)
+rt3 TE-IS 50 rt3 - rt2(4)
+rt2
+rt5 TE-IS 60 rt3 - rt3(4)
+2001:db8::1/128 IP6 internal 60 rt1 - rt1(4)
+2001:db8::3/128 IP6 internal 60 rt3 - rt3(4)
+rt4 TE-IS 70 rt3 - rt5(4)
+rt6 TE-IS 70 rt3 - rt5(4)
+2001:db8::5/128 IP6 internal 70 rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 80 rt3 - rt4(4)
+2001:db8::6/128 IP6 internal 80 rt3 - rt6(4)
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+2001:db8::2/128 IP6 internal 0 rt2(4)
+rt4 TE-IS 10 rt4 - rt2(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+rt6 TE-IS 20 rt4 - rt4(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+rt3 TE-IS 30 rt4 - rt5(4)
+2001:db8::5/128 IP6 internal 30 rt4 - rt5(4)
+2001:db8::6/128 IP6 internal 30 rt4 - rt6(4)
+rt2
+rt1 TE-IS 40 rt4 - rt2(2)
+2001:db8::3/128 IP6 internal 40 rt4 - rt3(4)
+2001:db8::1/128 IP6 internal 50 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 50 - rt4 16011
+ 2001:db8::2/128 0 - - -
+ 2001:db8::3/128 40 - rt4 16031
+ 2001:db8::4/128 20 - rt4 implicit-null
+ 2001:db8::5/128 30 - rt4 16051
+ 2001:db8::6/128 30 - rt4 16061
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 50 - rt1 implicit-null
+ - rt3 16011
+ 2001:db8::3/128 50 - rt1 16031
+ - rt3 implicit-null
+ 2001:db8::4/128 80 - rt3 50500/16041
+ 2001:db8::5/128 60 - rt1 16051
+ - rt3 16051
+ 2001:db8::6/128 70 - rt3 16061
+
+test# test isis topology 13 root rt1 remote-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt2
+
+P-space (rt2):
+ rt2
+ rt4
+
+Q-space:
+ rt3
+ rt4
+ rt5
+ rt6
+ rt7
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+rt3 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+rt5 TE-IS 40 rt2 - rt3(4)
+rt6 TE-IS 40 rt2 - rt3(4)
+10.0.255.3/32 IP TE 40 rt2 - rt3(4)
+rt7 TE-IS 50 rt2 - rt5(4)
+ rt6(4)
+10.0.255.5/32 IP TE 50 rt2 - rt5(4)
+10.0.255.6/32 IP TE 50 rt2 - rt6(4)
+10.0.255.7/32 IP TE 60 rt2 - rt7(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+ rt3 - rt3(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+ rt6(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+ rt3 -
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ - rt3 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 30 - rt3 16060
+ 10.0.255.7/32 40 - rt3 16070
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.3/32 40 - rt2 50400/16030
+ 10.0.255.5/32 50 - rt2 50400/16050
+ 10.0.255.6/32 50 - rt2 50400/16060
+ 10.0.255.7/32 60 - rt2 50400/16070
+
+test#
test# test isis topology 1 root rt1 ti-lfa system-id rt2
P-space (self):
rt3
diff --git a/tests/lib/test_xref.c b/tests/lib/test_xref.c new file mode 100644 index 0000000000..700950de1f --- /dev/null +++ b/tests/lib/test_xref.c @@ -0,0 +1,140 @@ +/* + * xref tests + * Copyright (C) 2020 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include "xref.h" +#include "log.h" + +/* + * "lib/test_xref.c" (only 1 directory component included) + * "logging call" + * 0x00000003 (network byte order - LOG_ERR) + * 0x00000000 (network byte order - EC / zero here) + * + * note there are no '\0' terminators included for the strings + * + * SHA256 + * => 71a65ce6e81517f642c8f55fb2af6f181f7df54357913b5b577aa61a663fdd4c + * & 0f -> 0x01 'H' + * & f001 -> 0x07 '7' + * & 3e -> 0x13 'K' + * & c007 -> 0x12 'J' + * & f8 -> 0x0b 'B' + * etc. + * (for reference: base32ch[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ") + * + * (bits are consumed starting with the lowest bit, and the first character + * only consumes 4 bits and has the 5th bit at 1) + */ + +static const char *expect_uid = "H7KJB-67TBH"; +static bool test_logcall(void) +{ + zlog_err("logging call"); + + return true; +} + +static void check_xref(const struct xref *xref, bool *found, bool *error) +{ + const char *file = xref->file, *p; + + p = strrchr(file, '/'); + if (p) + file = p + 1; + + if (strcmp(file, "test_xref.c")) + return; + if (xref->type != XREFT_LOGMSG) + return; + if (strcmp(xref->func, "test_logcall")) + return; + + printf("xref: %s:%d %s() type=%d uid=%s\n", + xref->file, xref->line, xref->func, xref->type, + xref->xrefdata ? xref->xrefdata->uid : "--"); + + if (*found) { + printf("duplicate xref!\n"); + *error = true; + } + + const struct xref_logmsg *logmsg; + + logmsg = container_of(xref, struct xref_logmsg, xref); + if (strcmp(logmsg->fmtstring, "logging call")) { + printf("log message mismatch!\n"); + *error = true; + } + if (logmsg->priority != LOG_ERR || logmsg->ec != 0) { + printf("metadata mismatch!\n"); + *error = true; + } + + *found = true; + + if (!xref->xrefdata) { + printf("no unique ID?\n"); + *error = true; + return; + } + + if (strcmp(xref->xrefdata->uid, expect_uid)) { + printf("unique ID mismatch, expected %s, got %s\n", + expect_uid, xref->xrefdata->uid); + *error = true; + } +} + +static bool test_lookup(void) +{ + struct xref_block *xb; + bool found = false, error = false; + + for (xb = xref_blocks; xb; xb = xb->next) { + const struct xref * const *xrefp; + + for (xrefp = xb->start; xrefp < xb->stop; xrefp++) { + const struct xref *xref = *xrefp; + + if (!xref) + continue; + + check_xref(xref, &found, &error); + } + } + return found && !error; +} + +bool (*tests[])(void) = { + test_lookup, + test_logcall, +}; + +XREF_SETUP() + +int main(int argc, char **argv) +{ + zlog_aux_init("NONE: ", ZLOG_DISABLED); + + for (unsigned int i = 0; i < array_size(tests); i++) + if (!tests[i]()) + return 1; + return 0; +} diff --git a/tests/lib/test_xref.py b/tests/lib/test_xref.py new file mode 100644 index 0000000000..8c3db3e182 --- /dev/null +++ b/tests/lib/test_xref.py @@ -0,0 +1,6 @@ +import frrtest + +class TestXref(frrtest.TestMultiOut): + program = './test_xref' + +TestXref.exit_cleanly() diff --git a/tests/ospfd/.gitignore b/tests/ospfd/.gitignore new file mode 100644 index 0000000000..c659b645db --- /dev/null +++ b/tests/ospfd/.gitignore @@ -0,0 +1,3 @@ +/*_afl/* +test_ospf_spf +core diff --git a/tests/ospfd/common.c b/tests/ospfd/common.c new file mode 100644 index 0000000000..eb30c4016e --- /dev/null +++ b/tests/ospfd/common.c @@ -0,0 +1,248 @@ +#include <zebra.h> + +#include "lib/stream.h" +#include "lib/vty.h" +#include "lib/mpls.h" +#include "lib/if.h" +#include "lib/table.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_sr.h" + +#include "common.h" + +struct thread_master *master; +struct zebra_privs_t ospfd_privs; + + +struct ospf_topology *test_find_topology(const char *name) +{ + if (strmatch(name, "topo1")) + return &topo1; + else if (strmatch(name, "topo2")) + return &topo2; + else if (strmatch(name, "topo3")) + return &topo3; + else if (strmatch(name, "topo4")) + return &topo4; + else if (strmatch(name, "topo5")) + return &topo5; + + return NULL; +} + +int sort_paths(const void **path1, const void **path2) +{ + const struct ospf_path *p1 = *path1; + const struct ospf_path *p2 = *path2; + + return (p1->nexthop.s_addr - p2->nexthop.s_addr); +} + +void print_route_table(struct vty *vty, struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route * or ; + struct listnode *pnode; + struct ospf_path *path; + struct mpls_label_stack *label_stack; + char buf[MPLS_LABEL_STRLEN]; + + for (rn = route_top(rt); rn; rn = route_next(rn)) { + if ((or = rn->info) == NULL) + continue; + + vty_out(vty, "N %-18pFX %-15pI4 %d\n", &rn->p, + & or->u.std.area_id, or->cost); + + list_sort(or->paths, sort_paths); + + for (ALL_LIST_ELEMENTS_RO(or->paths, pnode, path)) { + if (path->nexthop.s_addr == 0) + continue; + + vty_out(vty, " -> %pI4 with adv router %pI4", + &path->nexthop, &path->adv_router); + + if (path->srni.backup_label_stack) { + label_stack = path->srni.backup_label_stack; + mpls_label2str(label_stack->num_labels, + label_stack->label, buf, + MPLS_LABEL_STRLEN, true); + vty_out(vty, " and backup path %s", buf); + } + vty_out(vty, "\n"); + } + } +} + +struct ospf_test_node *test_find_node(struct ospf_topology *topology, + const char *hostname) +{ + for (int i = 0; topology->nodes[i].hostname[0]; i++) + if (strmatch(hostname, topology->nodes[i].hostname)) + return &topology->nodes[i]; + + return NULL; +} + +static void inject_router_lsa(struct vty *vty, struct ospf *ospf, + struct ospf_topology *topology, + struct ospf_test_node *root, + struct ospf_test_node *tnode) +{ + struct ospf_area *area; + struct in_addr router_id; + struct in_addr adj_router_id; + struct prefix_ipv4 prefix; + struct in_addr data; + struct stream *s; + struct lsa_header *lsah; + struct ospf_lsa *new; + int length; + unsigned long putp; + uint16_t link_count; + struct ospf_test_node *tfound_adj_node; + struct ospf_test_adj *tadj; + bool is_self_lsa = false; + + area = ospf->backbone; + inet_aton(tnode->router_id, &router_id); + + if (strncmp(root->router_id, tnode->router_id, 256) == 0) + is_self_lsa = true; + + s = stream_new(OSPF_MAX_LSA_SIZE); + lsa_header_set(s, LSA_OPTIONS_GET(area) | LSA_OPTIONS_NSSA_GET(area), + OSPF_ROUTER_LSA, router_id, router_id); + + stream_putc(s, router_lsa_flags(area)); + stream_putc(s, 0); + + putp = stream_get_endp(s); + stream_putw(s, 0); + + for (link_count = 0; tnode->adjacencies[link_count].hostname[0]; + link_count++) { + tadj = &tnode->adjacencies[link_count]; + tfound_adj_node = test_find_node(topology, tadj->hostname); + str2prefix_ipv4(tnode->adjacencies[link_count].network, + &prefix); + + inet_aton(tfound_adj_node->router_id, &adj_router_id); + data.s_addr = prefix.prefix.s_addr; + link_info_set(&s, adj_router_id, data, + LSA_LINK_TYPE_POINTOPOINT, 0, tadj->metric); + + masklen2ip(prefix.prefixlen, &data); + link_info_set(&s, prefix.prefix, data, LSA_LINK_TYPE_STUB, 0, + tadj->metric); + } + + /* Don't forget the node itself (just a stub) */ + str2prefix_ipv4(tnode->router_id, &prefix); + data.s_addr = 0xffffffff; + link_info_set(&s, prefix.prefix, data, LSA_LINK_TYPE_STUB, 0, 0); + + /* Take twice the link count (for P2P and stub) plus the local stub */ + stream_putw_at(s, putp, (2 * link_count) + 1); + + length = stream_get_endp(s); + lsah = (struct lsa_header *)STREAM_DATA(s); + lsah->length = htons(length); + + new = ospf_lsa_new_and_data(length); + new->area = area; + new->vrf_id = area->ospf->vrf_id; + + if (is_self_lsa) + SET_FLAG(new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED); + + memcpy(new->data, lsah, length); + stream_free(s); + + ospf_lsdb_add(area->lsdb, new); + + if (is_self_lsa) { + ospf_lsa_unlock(&area->router_lsa_self); + area->router_lsa_self = ospf_lsa_lock(new); + } +} + +static void inject_sr_db_entry(struct vty *vty, struct ospf_test_node *tnode, + struct ospf_topology *topology) +{ + struct ospf_test_node *tfound_adj_node; + struct ospf_test_adj *tadj; + struct in_addr router_id; + struct in_addr remote_id; + struct sr_node *srn; + struct sr_prefix *srp; + struct sr_link *srl; + int link_count; + + inet_aton(tnode->router_id, &router_id); + + srn = ospf_sr_node_create(&router_id); + + srn->srgb.range_size = 8000; + srn->srgb.lower_bound = 16000; + srn->msd = 16; + + srn->srlb.range_size = 1000; + srn->srlb.lower_bound = 15000; + + /* Prefix SID */ + srp = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix)); + srp->adv_router = router_id; + srp->sid = tnode->label; + srp->srn = srn; + + listnode_add(srn->ext_prefix, srp); + + /* Adjacency SIDs for all adjacencies */ + for (link_count = 0; tnode->adjacencies[link_count].hostname[0]; + link_count++) { + tadj = &tnode->adjacencies[link_count]; + tfound_adj_node = test_find_node(topology, tadj->hostname); + + srl = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_link)); + srl->adv_router = router_id; + + inet_aton(tfound_adj_node->router_id, &remote_id); + srl->remote_id = remote_id; + + srl->type = ADJ_SID; + srl->sid[0] = srn->srlb.lower_bound + tadj->label; + srl->srn = srn; + + listnode_add(srn->ext_link, srl); + } +} + +int topology_load(struct vty *vty, struct ospf_topology *topology, + struct ospf_test_node *root, struct ospf *ospf) +{ + struct ospf_test_node *tnode; + + for (int i = 0; topology->nodes[i].hostname[0]; i++) { + tnode = &topology->nodes[i]; + + /* Inject a router LSA for each node, used for SPF */ + inject_router_lsa(vty, ospf, topology, root, tnode); + + /* + * SR information could also be inected via LSAs, but directly + * filling the SR DB with labels is just easier. + */ + inject_sr_db_entry(vty, tnode, topology); + } + + return 0; +} diff --git a/tests/ospfd/common.h b/tests/ospfd/common.h new file mode 100644 index 0000000000..6d3e63e359 --- /dev/null +++ b/tests/ospfd/common.h @@ -0,0 +1,47 @@ +#ifndef _COMMON_OSPF_H +#define _COMMON_OSPF_H + +#define MAX_ADJACENCIES 8 +#define MAX_NODES 12 + +struct ospf_test_adj { + char hostname[256]; + char network[256]; + uint32_t metric; + mpls_label_t label; +}; + +struct ospf_test_node { + char hostname[256]; + const char *router_id; + mpls_label_t label; + struct ospf_test_adj adjacencies[MAX_ADJACENCIES + 1]; +}; + +struct ospf_topology { + struct ospf_test_node nodes[MAX_NODES + 1]; +}; + +/* Prototypes. */ +extern struct ospf_topology *test_find_topology(const char *name); +extern struct ospf_test_node *test_find_node(struct ospf_topology *topology, + const char *hostname); +extern int topology_load(struct vty *vty, struct ospf_topology *topology, + struct ospf_test_node *root, struct ospf *ospf); + +/* Global variables. */ +extern struct thread_master *master; +extern struct ospf_topology topo1; +extern struct ospf_topology topo2; +extern struct ospf_topology topo3; +extern struct ospf_topology topo4; +extern struct ospf_topology topo5; +extern struct zebra_privs_t ospfd_privs; + +/* For stable order in unit tests */ +extern int sort_paths(const void **path1, const void **path2); + +/* Print the routing table */ +extern void print_route_table(struct vty *vty, struct route_table *rt); + +#endif /* _COMMON_OSPF_H */ diff --git a/tests/ospfd/test_ospf_spf.c b/tests/ospfd/test_ospf_spf.c new file mode 100644 index 0000000000..a85f7e14ec --- /dev/null +++ b/tests/ospfd/test_ospf_spf.c @@ -0,0 +1,303 @@ +#include <zebra.h> + +#include "getopt.h" +#include "thread.h" +#include <lib/version.h> +#include "vty.h" +#include "command.h" +#include "log.h" +#include "vrf.h" +#include "table.h" +#include "mpls.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_ti_lfa.h" +#include "ospfd/ospf_vty.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_sr.h" + +#include "common.h" + +DECLARE_RBTREE_UNIQ(p_spaces, struct p_space, p_spaces_item, + p_spaces_compare_func) +DECLARE_RBTREE_UNIQ(q_spaces, struct q_space, q_spaces_item, + q_spaces_compare_func) + +static struct ospf *test_init(struct ospf_test_node *root) +{ + struct ospf *ospf; + struct ospf_area *area; + struct in_addr area_id; + struct in_addr router_id; + + ospf = ospf_new_alloc(0, NULL); + + area_id.s_addr = OSPF_AREA_BACKBONE; + area = ospf_area_new(ospf, area_id); + listnode_add_sort(ospf->areas, area); + + inet_aton(root->router_id, &router_id); + ospf->router_id = router_id; + ospf->router_id_static = router_id; + ospf->ti_lfa_enabled = true; + + return ospf; +} + +static void test_run_spf(struct vty *vty, struct ospf *ospf, + enum protection_type protection_type, bool verbose) +{ + struct route_table *new_table, *new_rtrs; + struct ospf_area *area; + struct p_space *p_space; + struct q_space *q_space; + char label_buf[MPLS_LABEL_STRLEN]; + char res_buf[PROTECTED_RESOURCE_STRLEN]; + + /* Just use the backbone for testing */ + area = ospf->backbone; + + new_table = route_table_init(); + new_rtrs = route_table_init(); + + /* dryrun true, root_node false */ + ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs, + true, false); + + if (verbose) { + vty_out(vty, "SPF Tree without TI-LFA backup paths:\n\n"); + ospf_spf_print(vty, area->spf, 0); + + vty_out(vty, + "\nRouting Table without TI-LFA backup paths:\n\n"); + print_route_table(vty, new_table); + } + + if (verbose) + vty_out(vty, "\n... generating TI-LFA backup paths ...\n"); + + /* TI-LFA testrun */ + ospf_ti_lfa_generate_p_spaces(area, protection_type); + ospf_ti_lfa_insert_backup_paths(area, new_table); + + /* Print P/Q space information */ + if (verbose) { + vty_out(vty, "\nP and Q space info:\n"); + frr_each (p_spaces, area->p_spaces, p_space) { + ospf_print_protected_resource( + p_space->protected_resource, res_buf); + vty_out(vty, "\nP Space for root %pI4 and %s\n", + &p_space->root->id, res_buf); + ospf_spf_print(vty, p_space->root, 0); + + frr_each (q_spaces, p_space->q_spaces, q_space) { + vty_out(vty, + "\nQ Space for destination %pI4:\n", + &q_space->root->id); + ospf_spf_print(vty, q_space->root, 0); + if (q_space->label_stack) { + mpls_label2str( + q_space->label_stack + ->num_labels, + q_space->label_stack->label, + label_buf, MPLS_LABEL_STRLEN, + true); + vty_out(vty, "\nLabel stack: %s\n", + label_buf); + } else { + vty_out(vty, + "\nLabel stack not generated!\n"); + } + } + + vty_out(vty, "\nPost-convergence SPF Tree:\n"); + ospf_spf_print(vty, p_space->pc_spf, 0); + } + } + + /* Cleanup */ + ospf_ti_lfa_free_p_spaces(area); + ospf_spf_cleanup(area->spf, area->spf_vertex_list); + + /* + * Print the new routing table which is augmented with TI-LFA backup + * paths (label stacks). + */ + if (verbose) + vty_out(vty, + "\n\nFinal Routing Table including backup paths:\n\n"); + + print_route_table(vty, new_table); +} + +static int test_run(struct vty *vty, struct ospf_topology *topology, + struct ospf_test_node *root, + enum protection_type protection_type, bool verbose) +{ + struct ospf *ospf; + + ospf = test_init(root); + + /* Inject LSAs into the OSPF backbone according to the topology */ + if (topology_load(vty, topology, root, ospf)) { + vty_out(vty, "%% Failed to load topology\n"); + return CMD_WARNING; + } + + if (verbose) { + vty_out(vty, "\n"); + show_ip_ospf_database_summary(vty, ospf, 0, NULL); + } + + test_run_spf(vty, ospf, protection_type, verbose); + + return 0; +} + +DEFUN(test_ospf, test_ospf_cmd, + "test ospf topology WORD root HOSTNAME ti-lfa [node-protection] [verbose]", + "Test mode\n" + "Choose OSPF for SPF testing\n" + "Network topology to choose\n" + "Name of the network topology to choose\n" + "Root node to choose\n" + "Hostname of the root node to choose\n" + "Use Topology-Independent LFA\n" + "Use node protection (default is link protection)\n" + "Verbose output\n") +{ + struct ospf_topology *topology; + struct ospf_test_node *root; + enum protection_type protection_type = OSPF_TI_LFA_LINK_PROTECTION; + int idx = 0; + bool verbose = false; + + /* Parse topology. */ + argv_find(argv, argc, "topology", &idx); + topology = test_find_topology(argv[idx + 1]->arg); + if (!topology) { + vty_out(vty, "%% Topology not found\n"); + return CMD_WARNING; + } + + argv_find(argv, argc, "root", &idx); + root = test_find_node(topology, argv[idx + 1]->arg); + if (!root) { + vty_out(vty, "%% Root not found\n"); + return CMD_WARNING; + } + + if (argv_find(argv, argc, "node-protection", &idx)) + protection_type = OSPF_TI_LFA_NODE_PROTECTION; + + if (argv_find(argv, argc, "verbose", &idx)) + verbose = true; + + return test_run(vty, topology, root, protection_type, verbose); +} + +static void vty_do_exit(int isexit) +{ + printf("\nend.\n"); + + cmd_terminate(); + vty_terminate(); + thread_master_free(master); + + if (!isexit) + exit(0); +} + +struct option longopts[] = {{"help", no_argument, NULL, 'h'}, + {"debug", no_argument, NULL, 'd'}, + {0} }; + +/* Help information display. */ +static void usage(char *progname, int status) +{ + if (status != 0) + fprintf(stderr, "Try `%s --help' for more information.\n", + progname); + else { + printf("Usage : %s [OPTION...]\n\ +ospfd SPF test program.\n\n\ +-u, --debug Enable debugging\n\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", + progname, FRR_BUG_ADDRESS); + } + exit(status); +} + +int main(int argc, char **argv) +{ + char *p; + char *progname; + struct thread thread; + bool debug = false; + + /* Set umask before anything for security */ + umask(0027); + + /* get program name */ + progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); + + while (1) { + int opt; + + opt = getopt_long(argc, argv, "hd", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + case 'd': + debug = true; + break; + case 'h': + usage(progname, 0); + break; + default: + usage(progname, 1); + break; + } + } + + /* master init. */ + master = thread_master_create(NULL); + + /* Library inits. */ + cmd_init(1); + cmd_hostname_set("test"); + vty_init(master, false); + if (debug) + zlog_aux_init("NONE: ", LOG_DEBUG); + else + zlog_aux_init("NONE: ", ZLOG_DISABLED); + + /* Install test command. */ + install_element(VIEW_NODE, &test_ospf_cmd); + + /* needed for SR DB init */ + ospf_vty_init(); + ospf_sr_init(); + + term_debug_ospf_ti_lfa = 1; + + /* Read input from .in file. */ + vty_stdio(vty_do_exit); + + /* Fetch next active thread. */ + while (thread_fetch(master, &thread)) + thread_call(&thread); + + /* Not reached. */ + exit(0); +} diff --git a/tests/ospfd/test_ospf_spf.in b/tests/ospfd/test_ospf_spf.in new file mode 100644 index 0000000000..f1e746745f --- /dev/null +++ b/tests/ospfd/test_ospf_spf.in @@ -0,0 +1,10 @@ +test ospf topology topo1 root rt1 ti-lfa +test ospf topology topo1 root rt1 ti-lfa node-protection +test ospf topology topo2 root rt1 ti-lfa +test ospf topology topo2 root rt1 ti-lfa node-protection +test ospf topology topo3 root rt1 ti-lfa +test ospf topology topo3 root rt1 ti-lfa node-protection +test ospf topology topo4 root rt1 ti-lfa +test ospf topology topo4 root rt1 ti-lfa node-protection +test ospf topology topo5 root rt1 ti-lfa +test ospf topology topo5 root rt1 ti-lfa node-protection diff --git a/tests/ospfd/test_ospf_spf.py b/tests/ospfd/test_ospf_spf.py new file mode 100644 index 0000000000..92a1c6a145 --- /dev/null +++ b/tests/ospfd/test_ospf_spf.py @@ -0,0 +1,4 @@ +import frrtest + +class TestOspfSPF(frrtest.TestRefOut): + program = './test_ospf_spf' diff --git a/tests/ospfd/test_ospf_spf.refout b/tests/ospfd/test_ospf_spf.refout new file mode 100644 index 0000000000..d1e3c7bc65 --- /dev/null +++ b/tests/ospfd/test_ospf_spf.refout @@ -0,0 +1,130 @@ +test# test ospf topology topo1 root rt1 ti-lfa +N 1.1.1.1/32 0.0.0.0 0 +N 2.2.2.2/32 0.0.0.0 10 + -> 10.0.1.2 with adv router 2.2.2.2 and backup path 15002 +N 3.3.3.3/32 0.0.0.0 10 + -> 10.0.3.2 with adv router 3.3.3.3 and backup path 15001 +N 10.0.1.0/24 0.0.0.0 10 +N 10.0.2.0/24 0.0.0.0 20 + -> 10.0.1.2 with adv router 2.2.2.2 and backup path 15002 + -> 10.0.3.2 with adv router 3.3.3.3 and backup path 15001 +N 10.0.3.0/24 0.0.0.0 10 +test# test ospf topology topo1 root rt1 ti-lfa node-protection +N 1.1.1.1/32 0.0.0.0 0 +N 2.2.2.2/32 0.0.0.0 10 + -> 10.0.1.2 with adv router 2.2.2.2 +N 3.3.3.3/32 0.0.0.0 10 + -> 10.0.3.2 with adv router 3.3.3.3 +N 10.0.1.0/24 0.0.0.0 10 +N 10.0.2.0/24 0.0.0.0 20 + -> 10.0.1.2 with adv router 2.2.2.2 + -> 10.0.3.2 with adv router 3.3.3.3 +N 10.0.3.0/24 0.0.0.0 10 +test# test ospf topology topo2 root rt1 ti-lfa +N 1.1.1.1/32 0.0.0.0 0 +N 2.2.2.2/32 0.0.0.0 10 + -> 10.0.1.2 with adv router 2.2.2.2 and backup path 15002 +N 3.3.3.3/32 0.0.0.0 20 + -> 10.0.1.2 with adv router 3.3.3.3 and backup path 15002 +N 10.0.1.0/24 0.0.0.0 10 +N 10.0.2.0/24 0.0.0.0 20 + -> 10.0.1.2 with adv router 2.2.2.2 and backup path 15002 +N 10.0.3.0/24 0.0.0.0 30 +test# test ospf topology topo2 root rt1 ti-lfa node-protection +N 1.1.1.1/32 0.0.0.0 0 +N 2.2.2.2/32 0.0.0.0 10 + -> 10.0.1.2 with adv router 2.2.2.2 +N 3.3.3.3/32 0.0.0.0 20 + -> 10.0.1.2 with adv router 3.3.3.3 and backup path 15002 +N 10.0.1.0/24 0.0.0.0 10 +N 10.0.2.0/24 0.0.0.0 20 + -> 10.0.1.2 with adv router 2.2.2.2 +N 10.0.3.0/24 0.0.0.0 30 +test# test ospf topology topo3 root rt1 ti-lfa +N 1.1.1.1/32 0.0.0.0 0 +N 2.2.2.2/32 0.0.0.0 10 + -> 10.0.1.2 with adv router 2.2.2.2 and backup path 16030 +N 3.3.3.3/32 0.0.0.0 20 + -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001 +N 4.4.4.4/32 0.0.0.0 10 + -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004 +N 10.0.1.0/24 0.0.0.0 10 +N 10.0.2.0/24 0.0.0.0 30 + -> 10.0.1.2 with adv router 2.2.2.2 and backup path 16030 +N 10.0.3.0/24 0.0.0.0 20 + -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004 +N 10.0.4.0/24 0.0.0.0 10 +test# test ospf topology topo3 root rt1 ti-lfa node-protection +N 1.1.1.1/32 0.0.0.0 0 +N 2.2.2.2/32 0.0.0.0 10 + -> 10.0.1.2 with adv router 2.2.2.2 +N 3.3.3.3/32 0.0.0.0 20 + -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001 +N 4.4.4.4/32 0.0.0.0 10 + -> 10.0.4.2 with adv router 4.4.4.4 +N 10.0.1.0/24 0.0.0.0 10 +N 10.0.2.0/24 0.0.0.0 30 + -> 10.0.1.2 with adv router 2.2.2.2 +N 10.0.3.0/24 0.0.0.0 20 + -> 10.0.4.2 with adv router 4.4.4.4 +N 10.0.4.0/24 0.0.0.0 10 +test# test ospf topology topo4 root rt1 ti-lfa +N 1.1.1.1/32 0.0.0.0 0 +N 2.2.2.2/32 0.0.0.0 10 + -> 10.0.1.2 with adv router 2.2.2.2 and backup path 16030/15006 +N 3.3.3.3/32 0.0.0.0 20 + -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004 +N 4.4.4.4/32 0.0.0.0 10 + -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004 +N 10.0.1.0/24 0.0.0.0 10 +N 10.0.2.0/24 0.0.0.0 60 + -> 10.0.1.2 with adv router 2.2.2.2 and backup path 16030/15006 +N 10.0.3.0/24 0.0.0.0 20 + -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004 +N 10.0.4.0/24 0.0.0.0 10 +test# test ospf topology topo4 root rt1 ti-lfa node-protection +N 1.1.1.1/32 0.0.0.0 0 +N 2.2.2.2/32 0.0.0.0 10 + -> 10.0.1.2 with adv router 2.2.2.2 +N 3.3.3.3/32 0.0.0.0 20 + -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004 +N 4.4.4.4/32 0.0.0.0 10 + -> 10.0.4.2 with adv router 4.4.4.4 +N 10.0.1.0/24 0.0.0.0 10 +N 10.0.2.0/24 0.0.0.0 60 + -> 10.0.1.2 with adv router 2.2.2.2 +N 10.0.3.0/24 0.0.0.0 20 + -> 10.0.4.2 with adv router 4.4.4.4 +N 10.0.4.0/24 0.0.0.0 10 +test# test ospf topology topo5 root rt1 ti-lfa +N 1.1.1.1/32 0.0.0.0 0 +N 2.2.2.2/32 0.0.0.0 30 + -> 10.0.4.2 with adv router 2.2.2.2 and backup path 15001 +N 3.3.3.3/32 0.0.0.0 20 + -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004 +N 4.4.4.4/32 0.0.0.0 10 + -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004/15006 +N 10.0.1.0/24 0.0.0.0 40 + -> 10.0.4.2 with adv router 2.2.2.2 and backup path 15001 +N 10.0.2.0/24 0.0.0.0 30 + -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004 +N 10.0.3.0/24 0.0.0.0 20 + -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004/15006 +N 10.0.4.0/24 0.0.0.0 10 +test# test ospf topology topo5 root rt1 ti-lfa node-protection +N 1.1.1.1/32 0.0.0.0 0 +N 2.2.2.2/32 0.0.0.0 30 + -> 10.0.4.2 with adv router 2.2.2.2 and backup path 15001 +N 3.3.3.3/32 0.0.0.0 20 + -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004 +N 4.4.4.4/32 0.0.0.0 10 + -> 10.0.4.2 with adv router 4.4.4.4 +N 10.0.1.0/24 0.0.0.0 40 + -> 10.0.4.2 with adv router 2.2.2.2 and backup path 15001 +N 10.0.2.0/24 0.0.0.0 30 + -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004 +N 10.0.3.0/24 0.0.0.0 20 + -> 10.0.4.2 with adv router 4.4.4.4 +N 10.0.4.0/24 0.0.0.0 10 +test# +end. diff --git a/tests/ospfd/topologies.c b/tests/ospfd/topologies.c new file mode 100644 index 0000000000..2dc611ce96 --- /dev/null +++ b/tests/ospfd/topologies.c @@ -0,0 +1,575 @@ +#include <zebra.h> + +#include "mpls.h" +#include "if.h" + +#include "ospfd/ospfd.h" + +#include "common.h" + +/* + * +---------+ +---------+ + * | | | | + * | RT1 |eth-rt2 eth-rt1| RT2 | + * | 1.1.1.1 +---------------------+ 2.2.2.2 | + * | | 10.0.1.0/24 | | + * +---------+ +---------+ + * |eth-rt3 eth-rt3| + * | | + * |10.0.3.0/24 | + * | | + * |eth-rt1 | + * +---------+ | + * | |eth-rt2 10.0.2.0/24| + * | RT3 +--------------------------+ + * | 3.3.3.3 | + * | | + * +---------+ + * + * Link Protection: + * P and Q spaces overlap here, hence just one P/Q node regardless of which + * link is protected. Hence the backup label stack just has one label. + * + * Node Protection: + * Obviously no backup paths involved. + */ +struct ospf_topology topo1 = { + .nodes = + { + { + .hostname = "rt1", + .router_id = "1.1.1.1", + .label = 10, + .adjacencies = + { + { + .hostname = "rt2", + .network = + "10.0.1.1/24", + .metric = 10, + .label = 1, + }, + { + .hostname = "rt3", + .network = + "10.0.3.1/24", + .metric = 10, + .label = 2, + }, + }, + }, + { + .hostname = "rt2", + .router_id = "2.2.2.2", + .label = 20, + .adjacencies = + { + { + .hostname = "rt1", + .network = + "10.0.1.2/24", + .metric = 10, + .label = 3, + }, + { + .hostname = "rt3", + .network = + "10.0.2.1/24", + .metric = 10, + .label = 4, + }, + }, + }, + { + .hostname = "rt3", + .router_id = "3.3.3.3", + .label = 30, + .adjacencies = + { + { + .hostname = "rt1", + .network = + "10.0.3.2/24", + .metric = 10, + .label = 5, + }, + { + .hostname = "rt2", + .network = + "10.0.2.2/24", + .metric = 10, + .label = 6, + }, + }, + }, + }, +}; + + +/* + * +---------+ +---------+ + * | | | | + * | RT1 |eth-rt2 eth-rt1| RT2 | + * | 1.1.1.1 +---------------------+ 2.2.2.2 | + * | | 10.0.1.0/24 (10) | | + * +---------+ +---------+ + * |eth-rt3 eth-rt3| + * | | + * |10.0.3.0/24 (30) | + * | | + * |eth-rt1 | + * +---------+ | + * | |eth-rt2 10.0.2.0/24|(10) + * | RT3 +--------------------------+ + * | 3.3.3.3 | + * | | + * +---------+ + * + * Link Protection: + * Regarding the subnet 10.0.1.0/24, the P space of RT1 is just RT1 itself + * while the Q space of RT3 consists of RT3 and RT2. Hence the P and Q + * nodes are disjunct (tricky: the root node is the P node here). For the + * backup label stack just one label is necessary. + * + * Node Protection: + * For protected node RT2 and route from RT1 to RT3 there is just the backup + * path consisting of the label 15002. + */ +struct ospf_topology topo2 = { + .nodes = + { + { + .hostname = "rt1", + .router_id = "1.1.1.1", + .label = 10, + .adjacencies = + { + { + .hostname = "rt2", + .network = + "10.0.1.1/24", + .metric = 10, + .label = 1, + }, + { + .hostname = "rt3", + .network = + "10.0.3.1/24", + .metric = 30, + .label = 2, + }, + }, + }, + { + .hostname = "rt2", + .router_id = "2.2.2.2", + .label = 20, + .adjacencies = + { + { + .hostname = "rt1", + .network = + "10.0.1.2/24", + .metric = 10, + .label = 3, + }, + { + .hostname = "rt3", + .network = + "10.0.2.1/24", + .metric = 10, + .label = 4, + }, + }, + }, + { + .hostname = "rt3", + .router_id = "3.3.3.3", + .label = 30, + .adjacencies = + { + { + .hostname = "rt1", + .network = + "10.0.3.2/24", + .metric = 30, + .label = 5, + }, + { + .hostname = "rt2", + .network = + "10.0.2.2/24", + .metric = 10, + .label = 6, + }, + }, + }, + }, +}; + +/* + * +---------+ +---------+ + * | | | | + * | RT1 |eth-rt4 eth-rt1| RT4 | + * | 1.1.1.1 +---------------------+ 4.4.4.4 | + * | | 10.0.4.0/24 (10) | | + * +---------+ +---------+ + * |eth-rt2 eth-rt3| + * | | + * |10.0.1.0/24 (10) | + * | 10.0.3.0/24 (10) | + * |eth-rt1 eth-rt4| + * +---------+ +---------+ + * | |eth-rt3 eth-rt2| | + * | RT2 +---------------------+ RT3 | + * | 2.2.2.2 | 10.0.2.0/24 (20) | 3.3.3.3 | + * | | | | + * +---------+ +---------+ + * + * Link Protection: + * Regarding the protected subnet 10.0.4.0/24, the P and Q spaces for root RT1 + * and destination RT4 are disjunct and the P node is RT2 while RT3 is the Q + * node. Hence the backup label stack here is 16020/15004. Note that here the + * P and Q nodes are neither the root nor the destination nodes, so this is a + * case where you really need a label stack consisting of two labels. + * + * Node Protection: + * For the protected node RT4 and the route from RT1 to RT3 there is a backup + * path with the single label 15001. + */ +struct ospf_topology topo3 = { + .nodes = + { + { + .hostname = "rt1", + .router_id = "1.1.1.1", + .label = 10, + .adjacencies = + { + { + .hostname = "rt2", + .network = + "10.0.1.1/24", + .metric = 10, + .label = 1, + }, + { + .hostname = "rt4", + .network = + "10.0.4.1/24", + .metric = 10, + .label = 2, + }, + }, + }, + { + .hostname = "rt2", + .router_id = "2.2.2.2", + .label = 20, + .adjacencies = + { + { + .hostname = "rt1", + .network = + "10.0.1.2/24", + .metric = 10, + .label = 3, + }, + { + .hostname = "rt3", + .network = + "10.0.2.1/24", + .metric = 20, + .label = 4, + }, + }, + }, + { + .hostname = "rt3", + .router_id = "3.3.3.3", + .label = 30, + .adjacencies = + { + { + .hostname = "rt2", + .network = + "10.0.2.2/24", + .metric = 20, + .label = 5, + }, + { + .hostname = "rt4", + .network = + "10.0.3.1/24", + .metric = 10, + .label = 6, + }, + }, + }, + { + .hostname = "rt4", + .router_id = "4.4.4.4", + .label = 40, + .adjacencies = + { + { + .hostname = "rt1", + .network = + "10.0.4.2/24", + .metric = 10, + .label = 7, + }, + { + .hostname = "rt3", + .network = + "10.0.3.2/24", + .metric = 10, + .label = 8, + }, + }, + }, + }, +}; + +/* + * +---------+ +---------+ + * | | | | + * | RT1 |eth-rt4 eth-rt1| RT4 | + * | 1.1.1.1 +---------------------+ 4.4.4.4 | + * | | 10.0.4.0/24 (10) | | + * +---------+ +---------+ + * |eth+rt2 eth-rt3| + * | | + * |10.0.1.0/24 (10) | + * | 10.0.3.0/24 (10) | + * |eth-rt1 eth-rt4| + * +---------+ +---------+ + * | |eth-rt3 eth-rt2| | + * | RT2 +---------------------+ RT3 | + * | 2.2.2.2 | 10.0.2.0/24 (40) | 3.3.3.3 | + * | | | | + * +---------+ +---------+ + * + * This case was specifically created for Node Protection with RT4 as + * protected node from the perspective of RT1. Note the weight of 40 + * on the link between RT2 and RT3. + * The P space of RT1 is just RT2 while the Q space of RT3 is empty. + * This means that the P and Q spaces are disjunct and there are two + * labels needed to get from RT1 to RT3. + */ +struct ospf_topology topo4 = { + .nodes = + { + { + .hostname = "rt1", + .router_id = "1.1.1.1", + .label = 10, + .adjacencies = + { + { + .hostname = "rt2", + .network = + "10.0.1.1/24", + .metric = 10, + .label = 1, + }, + { + .hostname = "rt4", + .network = + "10.0.4.1/24", + .metric = 10, + .label = 2, + }, + }, + }, + { + .hostname = "rt2", + .router_id = "2.2.2.2", + .label = 20, + .adjacencies = + { + { + .hostname = "rt1", + .network = + "10.0.1.2/24", + .metric = 10, + .label = 3, + }, + { + .hostname = "rt3", + .network = + "10.0.2.1/24", + .metric = 50, + .label = 4, + }, + }, + }, + { + .hostname = "rt3", + .router_id = "3.3.3.3", + .label = 30, + .adjacencies = + { + { + .hostname = "rt2", + .network = + "10.0.2.2/24", + .metric = 50, + .label = 5, + }, + { + .hostname = "rt4", + .network = + "10.0.3.1/24", + .metric = 10, + .label = 6, + }, + }, + }, + { + .hostname = "rt4", + .router_id = "4.4.4.4", + .label = 40, + .adjacencies = + { + { + .hostname = "rt3", + .network = + "10.0.3.2/24", + .metric = 10, + .label = 7, + }, + { + .hostname = "rt1", + .network = + "10.0.4.2/24", + .metric = 10, + .label = 8, + }, + }, + }, + }, +}; + +/* + * +---------+ +---------+ + * | | | | + * | RT1 |eth-rt4 eth-rt1| RT4 | + * | 1.1.1.1 +---------------------+ 4.4.4.4 | + * | | 10.0.4.0/24 | | + * +---------+ +---------+ + * |eth+rt2 eth-rt3| + * | | + * |10.0.1.0/24 | + * | 10.0.3.0/24| + * |eth-rt1 eth-rt4| + * +---------+ +---------+ + * | |eth-rt3 eth-rt2| | + * | RT2 +---------------------+ RT3 | + * | 2.2.2.2 | 10.0.2.0/24 | 3.3.3.3 | + * | | | | + * +---------+ +---------+ + * + * Weights: + * - clockwise: 10 + * - counterclockwise: 40 + * + * This is an example where 3 (!) labels are needed for the protected + * link RT1<->RT2, e.g. the subnet 10.0.1.0/24, to reach RT4. + * + * Because the initial P and Q spaces will not be overlapping or + * adjacent for this case the TI-LFA will be applied recursively. + */ +struct ospf_topology topo5 = { + .nodes = + { + { + .hostname = "rt1", + .router_id = "1.1.1.1", + .label = 10, + .adjacencies = + { + { + .hostname = "rt2", + .network = + "10.0.1.1/24", + .metric = 40, + .label = 1, + }, + { + .hostname = "rt4", + .network = + "10.0.4.1/24", + .metric = 10, + .label = 2, + }, + }, + }, + { + .hostname = "rt2", + .router_id = "2.2.2.2", + .label = 20, + .adjacencies = + { + { + .hostname = "rt1", + .network = + "10.0.1.2/24", + .metric = 10, + .label = 3, + }, + { + .hostname = "rt3", + .network = + "10.0.2.1/24", + .metric = 40, + .label = 4, + }, + }, + }, + { + .hostname = "rt3", + .router_id = "3.3.3.3", + .label = 30, + .adjacencies = + { + { + .hostname = "rt2", + .network = + "10.0.2.2/24", + .metric = 10, + .label = 5, + }, + { + .hostname = "rt4", + .network = + "10.0.3.1/24", + .metric = 40, + .label = 6, + }, + }, + }, + { + .hostname = "rt4", + .router_id = "4.4.4.4", + .label = 40, + .adjacencies = + { + { + .hostname = "rt3", + .network = + "10.0.3.2/24", + .metric = 10, + .label = 7, + }, + { + .hostname = "rt1", + .network = + "10.0.4.2/24", + .metric = 40, + .label = 8, + }, + }, + }, + }, +}; diff --git a/tests/subdir.am b/tests/subdir.am index 211814c1c3..370e6a49a9 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -31,6 +31,14 @@ TESTS_ISISD = IGNORE_ISISD = --ignore=isisd/ endif +if OSPFD +TESTS_OSPFD = \ + tests/ospfd/test_ospf_spf \ + # end +else +TESTS_OSPFD = +endif + if OSPF6D TESTS_OSPF6D = \ tests/ospf6d/test_lsdb \ @@ -41,6 +49,16 @@ TESTS_OSPF6D = IGNORE_OSPF6D = --ignore=ospf6d/ endif +if ZEBRA +TESTS_ZEBRA = \ + tests/zebra/test_lm_plugin \ + #end +IGNORE_ZEBRA = +else +TESTS_ZEBRA = +IGNORE_ZEBRA = --ignore=zebra/ +endif + clippy_scan += \ tests/lib/cli/test_cli.c \ tests/ospf6d/test_lsdb.c \ @@ -73,6 +91,7 @@ check_PROGRAMS = \ tests/lib/test_ttable \ tests/lib/test_typelist \ tests/lib/test_versioncmp \ + tests/lib/test_xref \ tests/lib/test_zlog \ tests/lib/test_graph \ tests/lib/cli/test_cli \ @@ -80,7 +99,9 @@ check_PROGRAMS = \ tests/lib/northbound/test_oper_data \ $(TESTS_BGPD) \ $(TESTS_ISISD) \ + $(TESTS_OSPFD) \ $(TESTS_OSPF6D) \ + $(TESTS_ZEBRA) \ # end if ZEROMQ @@ -115,6 +136,7 @@ noinst_HEADERS += \ tests/lib/cli/common_cli.h \ tests/lib/test_typelist.h \ tests/isisd/test_common.h \ + tests/ospfd/common.h \ # end # @@ -134,7 +156,9 @@ TESTS_CFLAGS = \ ALL_TESTS_LDADD = lib/libfrr.la $(LIBCAP) BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) -lm ISISD_TEST_LDADD = isisd/libisis.a $(ALL_TESTS_LDADD) +OSPFD_TEST_LDADD = ospfd/libfrrospf.a $(ALL_TESTS_LDADD) OSPF6_TEST_LDADD = ospf6d/libospf6.a $(ALL_TESTS_LDADD) +ZEBRA_TEST_LDADD = zebra/label_manager.o $(ALL_TESTS_LDADD) tests_bgpd_test_aspath_CFLAGS = $(TESTS_CFLAGS) tests_bgpd_test_aspath_CPPFLAGS = $(TESTS_CPPFLAGS) @@ -201,6 +225,11 @@ tests_isisd_test_isis_vertex_queue_CPPFLAGS = $(TESTS_CPPFLAGS) tests_isisd_test_isis_vertex_queue_LDADD = $(ISISD_TEST_LDADD) tests_isisd_test_isis_vertex_queue_SOURCES = tests/isisd/test_isis_vertex_queue.c tests/isisd/test_common.c +tests_ospfd_test_ospf_spf_CFLAGS = $(TESTS_CFLAGS) +tests_ospfd_test_ospf_spf_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_ospfd_test_ospf_spf_LDADD = $(OSPFD_TEST_LDADD) +tests_ospfd_test_ospf_spf_SOURCES = tests/ospfd/test_ospf_spf.c tests/ospfd/common.c tests/ospfd/topologies.c + tests_lib_cxxcompat_CFLAGS = $(TESTS_CFLAGS) $(CXX_COMPAT_CFLAGS) $(WERROR) tests_lib_cxxcompat_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_cxxcompat_SOURCES = tests/lib/cxxcompat.c @@ -322,6 +351,10 @@ tests_lib_test_versioncmp_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_versioncmp_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_versioncmp_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_versioncmp_SOURCES = tests/lib/test_versioncmp.c +tests_lib_test_xref_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_xref_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_xref_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_xref_SOURCES = tests/lib/test_xref.c tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD) @@ -336,6 +369,11 @@ tests_ospf6d_test_lsdb_CPPFLAGS = $(TESTS_CPPFLAGS) tests_ospf6d_test_lsdb_LDADD = $(OSPF6_TEST_LDADD) tests_ospf6d_test_lsdb_SOURCES = tests/ospf6d/test_lsdb.c tests/lib/cli/common_cli.c +tests_zebra_test_lm_plugin_CFLAGS = $(TESTS_CFLAGS) +tests_zebra_test_lm_plugin_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_zebra_test_lm_plugin_LDADD = $(ZEBRA_TEST_LDADD) +tests_zebra_test_lm_plugin_SOURCES = tests/zebra/test_lm_plugin.c + EXTRA_DIST += \ tests/runtests.py \ tests/bgpd/test_aspath.py \ @@ -353,6 +391,9 @@ EXTRA_DIST += \ tests/isisd/test_isis_spf.in \ tests/isisd/test_isis_spf.refout \ tests/isisd/test_isis_vertex_queue.py \ + tests/ospfd/test_ospf_spf.py \ + tests/ospfd/test_ospf_spf.in \ + tests/ospfd/test_ospf_spf.refout \ tests/lib/cli/test_commands.in \ tests/lib/cli/test_commands.py \ tests/lib/cli/test_commands.refout \ @@ -377,12 +418,15 @@ EXTRA_DIST += \ tests/lib/test_ttable.refout \ tests/lib/test_typelist.py \ tests/lib/test_versioncmp.py \ + tests/lib/test_xref.py \ tests/lib/test_zlog.py \ tests/lib/test_graph.py \ tests/lib/test_graph.refout \ tests/ospf6d/test_lsdb.py \ tests/ospf6d/test_lsdb.in \ tests/ospf6d/test_lsdb.refout \ + tests/zebra/test_lm_plugin.py \ + tests/zebra/test_lm_plugin.refout \ # end .PHONY: tests/tests.xml diff --git a/tests/topotests/all-protocol-startup/r1/ip_nht.ref b/tests/topotests/all-protocol-startup/r1/ip_nht.ref index 098e3bf387..1da4da4df5 100644 --- a/tests/topotests/all-protocol-startup/r1/ip_nht.ref +++ b/tests/topotests/all-protocol-startup/r1/ip_nht.ref @@ -39,6 +39,18 @@ 4.4.4.2 unresolved Client list: pbr(fd XX) +6.6.6.1 + unresolved + Client list: pbr(fd XX) +6.6.6.2 + unresolved + Client list: pbr(fd XX) +6.6.6.3 + unresolved + Client list: pbr(fd XX) +6.6.6.4 + unresolved + Client list: pbr(fd XX) 192.168.0.2 resolved via connected is directly connected, r1-eth0 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 08378d9b58..5942aca71d 100644 --- a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py +++ b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py @@ -83,6 +83,9 @@ class NetworkTopo(Topo): ##################################################### +@pytest.mark.isis +@pytest.mark.ospf +@pytest.mark.rip def setup_module(module): global topo, net global fatal_error @@ -342,7 +345,7 @@ def test_converge_protocols(): actual = ( net["r%s" % i] .cmd( - 'vtysh -c "show ip route" | /usr/bin/tail -n +7 | env LC_ALL=en_US.UTF-8 sort 2> /dev/null' + 'vtysh -c "show ip route" | sed -e \'/^Codes: /,/^\s*$/d\' | env LC_ALL=en_US.UTF-8 sort 2> /dev/null' ) .rstrip() ) @@ -373,7 +376,7 @@ def test_converge_protocols(): actual = ( net["r%s" % i] .cmd( - 'vtysh -c "show ipv6 route" | /usr/bin/tail -n +7 | env LC_ALL=en_US.UTF-8 sort 2> /dev/null' + 'vtysh -c "show ipv6 route" | sed -e \'/^Codes: /,/^\s*$/d\' | env LC_ALL=en_US.UTF-8 sort 2> /dev/null' ) .rstrip() ) @@ -535,6 +538,51 @@ def test_nexthop_groups(): verify_route_nexthop_group("5.5.5.1/32") + ## 4-way ECMP Routes Pointing to Each Other + + # This is to check for a bug with NH resolution where + # routes would infintely resolve to each other blowing + # up the resolved-> nexthop pointer. + + net["r1"].cmd( + 'vtysh -c "c t" -c "nexthop-group infinite-recursive" -c "nexthop 6.6.6.1" -c "nexthop 6.6.6.2" \ + -c "nexthop 6.6.6.3" -c "nexthop 6.6.6.4"' + ) + + # static route nexthops can recurse to + + net["r1"].cmd('vtysh -c "c t" -c "ip route 6.6.6.0/24 1.1.1.1"') + + # Make routes that point to themselves in ecmp + + net["r1"].cmd( + 'vtysh -c "sharp install routes 6.6.6.4 nexthop-group infinite-recursive 1"' + ) + + net["r1"].cmd( + 'vtysh -c "sharp install routes 6.6.6.3 nexthop-group infinite-recursive 1"' + ) + + net["r1"].cmd( + 'vtysh -c "sharp install routes 6.6.6.2 nexthop-group infinite-recursive 1"' + ) + + net["r1"].cmd( + 'vtysh -c "sharp install routes 6.6.6.1 nexthop-group infinite-recursive 1"' + ) + + # Get routes and test if has too many (duplicate) nexthops + nhg_id = route_get_nhg_id("6.6.6.1/32") + output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id) + + dups = re.findall(r"(via 1\.1\.1\.1)", output) + + # Should find 3, itself is inactive + assert len(dups) == 3, ( + "Route 6.6.6.1/32 with Nexthop Group ID=%d has wrong number of resolved nexthops" + % nhg_id + ) + ##CLI(net) ## Remove all NHG routes @@ -546,6 +594,8 @@ def test_nexthop_groups(): net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.1 1"') net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.2 1"') net["r1"].cmd('vtysh -c "sharp remove routes 5.5.5.1 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 6.6.6.1 4"') + net["r1"].cmd('vtysh -c "c t" -c "no ip route 6.6.6.0/24 1.1.1.1"') def test_rip_status(): @@ -1016,6 +1066,7 @@ def test_bgp_ipv6_summary(): # For debugging after starting FRR daemons, uncomment the next line # CLI(net) + def test_nht(): print("\n\n**** Test that nexthop tracking is at least nominally working ****\n") @@ -1026,13 +1077,16 @@ def test_nht(): expected = open(nhtFile).read().rstrip() expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) - actual = (net["r%s" %i].cmd('vtysh -c "show ip nht" 2> /dev/null').rstrip()) + actual = net["r%s" % i].cmd('vtysh -c "show ip nht" 2> /dev/null').rstrip() actual = re.sub(r"fd [0-9][0-9]", "fd XX", actual) actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) - diff = topotest.get_textdiff(actual, expected, - title1="Actual `show ip nht`", - title2="Expected `show ip nht`") + diff = topotest.get_textdiff( + actual, + expected, + title1="Actual `show ip nht`", + title2="Expected `show ip nht`", + ) if diff: assert 0, "r%s failed ip nht check:\n%s\n" % (i, diff) @@ -1043,19 +1097,23 @@ def test_nht(): expected = open(nhtFile).read().rstrip() expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) - actual = (net["r%s" %i].cmd('vtysh -c "show ipv6 nht" 2> /dev/null').rstrip()) + actual = net["r%s" % i].cmd('vtysh -c "show ipv6 nht" 2> /dev/null').rstrip() actual = re.sub(r"fd [0-9][0-9]", "fd XX", actual) actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) - diff = topotest.get_textdiff(actual, expected, - title1="Actual `show ip nht`", - title2="Expected `show ip nht`") + diff = topotest.get_textdiff( + actual, + expected, + title1="Actual `show ip nht`", + title2="Expected `show ip nht`", + ) if diff: assert 0, "r%s failed ipv6 nht check:\n%s\n" % (i, diff) else: print("show ipv6 nht is ok\n") + def test_bgp_ipv4(): global fatal_error global net diff --git a/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py index 595132214b..cc1c1e3a0c 100644 --- a/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py +++ b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py @@ -64,7 +64,7 @@ class BFDTopo(Topo): switch.add_link(tgen.gears["r2"]) switch.add_link(tgen.gears["r3"]) - +@pytest.mark.bfd def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(BFDTopo, mod.__name__) @@ -74,7 +74,8 @@ def setup_module(mod): for rname, router in router_list.items(): router.load_config( - TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), + TopoRouter.RD_ZEBRA, + os.path.join(CWD, "{}/zebra.conf".format(rname)), ) router.load_config( TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) diff --git a/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py b/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py index 1adfec76d8..23da7ed850 100644 --- a/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py +++ b/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py @@ -127,7 +127,8 @@ class TemplateTopo(Topo): switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5") switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4") - +@pytest.mark.bfd +@pytest.mark.isis def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(TemplateTopo, mod.__name__) diff --git a/tests/topotests/bfd-ospf-topo1/__init__.py b/tests/topotests/bfd-ospf-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/__init__.py diff --git a/tests/topotests/bfd-ospf-topo1/rt1/bfdd.conf b/tests/topotests/bfd-ospf-topo1/rt1/bfdd.conf new file mode 100644 index 0000000000..610a20f88a --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/bfdd.conf @@ -0,0 +1,9 @@ +log file bfdd.log +log timestamp precision 3 +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd +! diff --git a/tests/topotests/bfd-ospf-topo1/rt1/ospf6d.conf b/tests/topotests/bfd-ospf-topo1/rt1/ospf6d.conf new file mode 100644 index 0000000000..18def599b4 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/ospf6d.conf @@ -0,0 +1,21 @@ +log file ospf6d.log +log timestamp precision 3 +! +hostname rt1 +! +password 1 +! +interface eth-rt2 + ipv6 ospf6 network broadcast + ipv6 ospf6 bfd +! +interface eth-rt3 + ipv6 ospf6 network broadcast + ipv6 ospf6 bfd +! +router ospf6 + ospf6 router-id 1.1.1.1 + interface eth-rt2 area 0.0.0.0 + interface eth-rt3 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/bfd-ospf-topo1/rt1/ospfd.conf b/tests/topotests/bfd-ospf-topo1/rt1/ospfd.conf new file mode 100644 index 0000000000..07b42f9885 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/ospfd.conf @@ -0,0 +1,26 @@ +log file ospfd.log +log timestamp precision 3 +! +hostname rt1 +! +password 1 +! +debug ospf event +debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 +! +interface eth-rt2 + ip ospf area 0.0.0.0 + ip ospf bfd +! +interface eth-rt3 + ip ospf area 0.0.0.0 + ip ospf bfd +! +router ospf + ospf router-id 1.1.1.1 + passive interface lo + router-info area 0.0.0.0 +! diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ip_route.ref b/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ip_route.ref new file mode 100644 index 0000000000..f354eff697 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ip_route.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..6465efb8b5 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ipv6_route.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step2/show_bfd_peers.ref b/tests/topotests/bfd-ospf-topo1/rt1/step2/show_bfd_peers.ref new file mode 100644 index 0000000000..63f0d50784 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step2/show_bfd_peers.ref @@ -0,0 +1,26 @@ +[ + { + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_healthy.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_healthy.ref new file mode 100644 index 0000000000..42051f9582 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_healthy.ref @@ -0,0 +1,28 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt2_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt2_down.ref new file mode 100644 index 0000000000..d844ee6813 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt2_down.ref @@ -0,0 +1,15 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt3_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt3_down.ref new file mode 100644 index 0000000000..32799084fb --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt3_down.ref @@ -0,0 +1,15 @@ +[ + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_healthy.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_healthy.ref new file mode 100644 index 0000000000..f354eff697 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_healthy.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt2_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt2_down.ref new file mode 100644 index 0000000000..43eecd0b7a --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt2_down.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt3_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt3_down.ref new file mode 100644 index 0000000000..409af6308b --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt3_down.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_healthy.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_healthy.ref new file mode 100644 index 0000000000..6465efb8b5 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_healthy.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt2_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt2_down.ref new file mode 100644 index 0000000000..cfb1ef1bb6 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt2_down.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt3_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt3_down.ref new file mode 100644 index 0000000000..58b44da5c2 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt3_down.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"ospf6", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-ospf-topo1/rt1/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt1/zebra.conf new file mode 100644 index 0000000000..6003125b6b --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt1/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +log timestamp precision 3 +! +hostname rt1 +! +debug zebra kernel +debug zebra packet +debug zebra events +debug zebra rib +! +interface lo + ip address 1.1.1.1/32 + ipv6 address ::ffff:0101:0101/128 +! +interface eth-rt2 + ip address 10.0.1.1/24 +! +interface eth-rt3 + ip address 10.0.2.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-ospf-topo1/rt2/bfdd.conf b/tests/topotests/bfd-ospf-topo1/rt2/bfdd.conf new file mode 100644 index 0000000000..437f063d8f --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt2/bfdd.conf @@ -0,0 +1,7 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd +! diff --git a/tests/topotests/bfd-ospf-topo1/rt2/ospf6d.conf b/tests/topotests/bfd-ospf-topo1/rt2/ospf6d.conf new file mode 100644 index 0000000000..2f35099564 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt2/ospf6d.conf @@ -0,0 +1,19 @@ +log file ospf6d.log +! +hostname rt2 +! +password 1 +! +interface eth-rt1 + ipv6 ospf6 network broadcast + ipv6 ospf6 bfd +! +interface eth-rt5 + ipv6 ospf6 network broadcast +! +router ospf6 + ospf6 router-id 2.2.2.2 + interface eth-rt1 area 0.0.0.0 + interface eth-rt5 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/bfd-ospf-topo1/rt2/ospfd.conf b/tests/topotests/bfd-ospf-topo1/rt2/ospfd.conf new file mode 100644 index 0000000000..a05d8b58c8 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt2/ospfd.conf @@ -0,0 +1,24 @@ +log file ospfd.log +! +hostname rt2 +! +password 1 +! +debug ospf event +debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 +! +interface eth-rt1 + ip ospf area 0.0.0.0 + ip ospf bfd +! +interface eth-rt5 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 2.2.2.2 + passive interface lo + router-info area 0.0.0.0 +! diff --git a/tests/topotests/bfd-ospf-topo1/rt2/step2/show_bfd_peers.ref b/tests/topotests/bfd-ospf-topo1/rt2/step2/show_bfd_peers.ref new file mode 100644 index 0000000000..d6df1ebfb2 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt2/step2/show_bfd_peers.ref @@ -0,0 +1,14 @@ +[ + { + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-ospf-topo1/rt2/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt2/zebra.conf new file mode 100644 index 0000000000..5fc7fc5b28 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt2/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt2 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 2.2.2.2/32 + ipv6 address ::ffff:0202:0202/128 +! +interface eth-rt1 + ip address 10.0.1.2/24 +! +interface eth-rt5 + ip address 10.0.3.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-ospf-topo1/rt3/bfdd.conf b/tests/topotests/bfd-ospf-topo1/rt3/bfdd.conf new file mode 100644 index 0000000000..437f063d8f --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt3/bfdd.conf @@ -0,0 +1,7 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd +! diff --git a/tests/topotests/bfd-ospf-topo1/rt3/ospf6d.conf b/tests/topotests/bfd-ospf-topo1/rt3/ospf6d.conf new file mode 100644 index 0000000000..3e8777019e --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt3/ospf6d.conf @@ -0,0 +1,19 @@ +log file ospf6d.log +! +hostname rt3 +! +password 1 +! +interface eth-rt1 + ipv6 ospf6 network broadcast + ipv6 ospf6 bfd +! +interface eth-rt4 + ipv6 ospf6 network broadcast +! +router ospf6 + ospf6 router-id 3.3.3.3 + interface eth-rt1 area 0.0.0.0 + interface eth-rt4 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/bfd-ospf-topo1/rt3/ospfd.conf b/tests/topotests/bfd-ospf-topo1/rt3/ospfd.conf new file mode 100644 index 0000000000..1196e6d189 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt3/ospfd.conf @@ -0,0 +1,24 @@ +log file ospfd.log +! +hostname rt3 +! +password 1 +! +debug ospf event +debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 +! +interface eth-rt1 + ip ospf area 0.0.0.0 + ip ospf bfd +! +interface eth-rt4 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 3.3.3.3 + passive interface lo + router-info area 0.0.0.0 +! diff --git a/tests/topotests/bfd-ospf-topo1/rt3/step2/show_bfd_peers.ref b/tests/topotests/bfd-ospf-topo1/rt3/step2/show_bfd_peers.ref new file mode 100644 index 0000000000..d6df1ebfb2 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt3/step2/show_bfd_peers.ref @@ -0,0 +1,14 @@ +[ + { + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-ospf-topo1/rt3/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt3/zebra.conf new file mode 100644 index 0000000000..d368de9bbe --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt3/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt3 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 3.3.3.3/32 + ipv6 address ::ffff:0303:0303/128 +! +interface eth-rt1 + ip address 10.0.2.2/24 +! +interface eth-rt4 + ip address 10.0.4.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-ospf-topo1/rt4/bfdd.conf b/tests/topotests/bfd-ospf-topo1/rt4/bfdd.conf new file mode 100644 index 0000000000..f35e772790 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt4/bfdd.conf @@ -0,0 +1,5 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! diff --git a/tests/topotests/bfd-ospf-topo1/rt4/ospf6d.conf b/tests/topotests/bfd-ospf-topo1/rt4/ospf6d.conf new file mode 100644 index 0000000000..bccd1e75bd --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt4/ospf6d.conf @@ -0,0 +1,18 @@ +log file ospf6d.log +! +hostname rt4 +! +password 1 +! +interface eth-rt3 + ipv6 ospf6 network broadcast +! +interface eth-rt5 + ipv6 ospf6 network broadcast +! +router ospf6 + ospf6 router-id 4.4.4.4 + interface eth-rt3 area 0.0.0.0 + interface eth-rt5 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/bfd-ospf-topo1/rt4/ospfd.conf b/tests/topotests/bfd-ospf-topo1/rt4/ospfd.conf new file mode 100644 index 0000000000..3a2568b4ab --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt4/ospfd.conf @@ -0,0 +1,23 @@ +log file ospfd.log +! +hostname rt4 +! +password 1 +! +debug ospf event +debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 +! +interface eth-rt3 + ip ospf area 0.0.0.0 +! +interface eth-rt5 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 4.4.4.4 + passive interface lo + router-info area 0.0.0.0 +! diff --git a/tests/topotests/bfd-ospf-topo1/rt4/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt4/zebra.conf new file mode 100644 index 0000000000..7b053bac35 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt4/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt4 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 4.4.4.4/32 + ipv6 address ::ffff:0404:0404/128 +! +interface eth-rt3 + ip address 10.0.4.2/24 +! +interface eth-rt5 + ip address 10.0.5.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-ospf-topo1/rt5/bfdd.conf b/tests/topotests/bfd-ospf-topo1/rt5/bfdd.conf new file mode 100644 index 0000000000..f35e772790 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt5/bfdd.conf @@ -0,0 +1,5 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! diff --git a/tests/topotests/bfd-ospf-topo1/rt5/ospf6d.conf b/tests/topotests/bfd-ospf-topo1/rt5/ospf6d.conf new file mode 100644 index 0000000000..766862276c --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt5/ospf6d.conf @@ -0,0 +1,18 @@ +log file ospf6d.log +! +hostname rt5 +! +password 1 +! +interface eth-rt2 + ipv6 ospf6 network broadcast +! +interface eth-rt4 + ipv6 ospf6 network broadcast +! +router ospf6 + ospf6 router-id 5.5.5.5 + interface eth-rt2 area 0.0.0.0 + interface eth-rt4 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/bfd-ospf-topo1/rt5/ospfd.conf b/tests/topotests/bfd-ospf-topo1/rt5/ospfd.conf new file mode 100644 index 0000000000..a35de5f45f --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt5/ospfd.conf @@ -0,0 +1,23 @@ +log file ospfd.log +! +hostname rt5 +! +password 1 +! +debug ospf event +debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 +! +interface eth-rt2 + ip ospf area 0.0.0.0 +! +interface eth-rt4 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 5.5.5.5 + passive interface lo + router-info area 0.0.0.0 +! diff --git a/tests/topotests/bfd-ospf-topo1/rt5/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt5/zebra.conf new file mode 100644 index 0000000000..0b7c9e02f3 --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/rt5/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt5 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 5.5.5.5/32 + ipv6 address ::ffff:0505:0505/128 +! +interface eth-rt2 + ip address 10.0.3.2/24 +! +interface eth-rt4 + ip address 10.0.5.2/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-ospf-topo1/test_bfd_ospf_topo1.py b/tests/topotests/bfd-ospf-topo1/test_bfd_ospf_topo1.py new file mode 100755 index 0000000000..1cec62789b --- /dev/null +++ b/tests/topotests/bfd-ospf-topo1/test_bfd_ospf_topo1.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python + +# +# test_bfd_ospf_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# 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_bfd_ospf_topo1.py: + + +---------+ + | | + eth-rt2 (.1) | RT1 | eth-rt3 (.1) + +----------+ 1.1.1.1 +----------+ + | | | | + | +---------+ | + | | + | 10.0.2.0/24 | + | | + | eth-rt1 | (.2) + | 10.0.1.0/24 +----+----+ + | | | + | | RT3 | + | | 3.3.3.3 | + | | | + (.2) | eth-rt1 +----+----+ + +----+----+ eth-rt4 | (.1) + | | | + | RT2 | | + | 2.2.2.2 | 10.0.4.0/24 | + | | | + +----+----+ | + (.1) | eth-rt5 eth-rt3 | (.2) + | +----+----+ + | | | + | | RT4 | + | | 4.4.4.4 | + | | | + | +----+----+ + | 10.0.3.0/24 eth-rt5 | (.1) + | | + | | + | 10.0.5.0/24 | + | | + | +---------+ | + | | | | + +----------+ RT5 +----------+ + eth-rt2 (.2) | 5.5.5.5 | eth-rt4 (.2) + | | + +---------+ + +""" + +import os +import sys +import pytest +import json +import re +from time import sleep +from time import time +from functools import partial + +# 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 + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["rt1", "rt2", "rt3", "rt4", "rt5"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1") + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1") + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt2") + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3") + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4") + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + 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_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def print_cmd_result(rname, command): + print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False)) + + +def router_compare_json_output(rname, command, reference, count=120, wait=0.5): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=count, wait=wait) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +## TEST STEPS + + +def test_rib_ospf_step1(): + logger.info("Test (step 1): verify RIB for OSPF") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_compare_json_output( + "rt1", "show ip route ospf json", "step1/show_ip_route.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route ospf json", "step1/show_ipv6_route.ref" + ) + + +def test_bfd_ospf_sessions_step2(): + logger.info("Test (step 2): verify BFD peers for OSPF") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # BFD is just used on three routers + for rt in ["rt1", "rt2", "rt3"]: + router_compare_json_output( + rt, "show bfd peers json", "step2/show_bfd_peers.ref" + ) + + +def test_bfd_ospf_interface_failure_rt2_step3(): + logger.info("Test (step 3): Check failover handling with RT2 down") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Let's kill the interface on rt2 and see what happens with the RIB and BFD on rt1 + tgen.gears["rt2"].link_enable("eth-rt1", enabled=False) + + # By default BFD provides a recovery time of 900ms plus jitter, so let's wait + # initial 2 seconds to let the CI not suffer. + # TODO: add check for array size + sleep(2) + router_compare_json_output( + "rt1", "show ip route ospf json", "step3/show_ip_route_rt2_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt2_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_rt2_down.ref", 1, 0 + ) + + # Check recovery, this can take some time + tgen.gears["rt2"].link_enable("eth-rt1", enabled=True) + + router_compare_json_output( + "rt1", "show ip route ospf json", "step3/show_ip_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" + ) + + +def test_bfd_ospf_interface_failure_rt3_step3(): + logger.info("Test (step 3): Check failover handling with RT3 down") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Let's kill the interface on rt3 and see what happens with the RIB and BFD on rt1 + tgen.gears["rt3"].link_enable("eth-rt1", enabled=False) + + # By default BFD provides a recovery time of 900ms plus jitter, so let's wait + # initial 2 seconds to let the CI not suffer. + # TODO: add check for array size + sleep(2) + router_compare_json_output( + "rt1", "show ip route ospf json", "step3/show_ip_route_rt3_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt3_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_rt3_down.ref", 1, 0 + ) + + # Check recovery, this can take some time + tgen.gears["rt3"].link_enable("eth-rt1", enabled=True) + + router_compare_json_output( + "rt1", "show ip route ospf json", "step3/show_ip_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" + ) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py index bd3b876eeb..6283f03ddf 100644 --- a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py +++ b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py @@ -77,7 +77,8 @@ class BFDProfTopo(Topo): switch.add_link(tgen.gears["r1"]) switch.add_link(tgen.gears["r6"]) - +@pytest.mark.bfd +@pytest.mark.isis def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(BFDProfTopo, mod.__name__) diff --git a/tests/topotests/bfd-topo1/test_bfd_topo1.py b/tests/topotests/bfd-topo1/test_bfd_topo1.py index 6e589d55eb..4c13fdcfc5 100644 --- a/tests/topotests/bfd-topo1/test_bfd_topo1.py +++ b/tests/topotests/bfd-topo1/test_bfd_topo1.py @@ -69,7 +69,7 @@ class BFDTopo(Topo): switch.add_link(tgen.gears["r2"]) switch.add_link(tgen.gears["r4"]) - +@pytest.mark.bfd def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(BFDTopo, mod.__name__) diff --git a/tests/topotests/bfd-topo2/test_bfd_topo2.py b/tests/topotests/bfd-topo2/test_bfd_topo2.py index feb4576bd3..5181a40f47 100644 --- a/tests/topotests/bfd-topo2/test_bfd_topo2.py +++ b/tests/topotests/bfd-topo2/test_bfd_topo2.py @@ -70,7 +70,7 @@ class BFDTopo(Topo): switch.add_link(tgen.gears["r2"]) switch.add_link(tgen.gears["r4"]) - +@pytest.mark.bfd def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(BFDTopo, mod.__name__) diff --git a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py index 5fed135f8d..956b526583 100644 --- a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py +++ b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py @@ -70,7 +70,7 @@ class BFDTopo(Topo): switch.add_link(tgen.gears["r2"]) switch.add_link(tgen.gears["r4"]) - +@pytest.mark.bfd def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(BFDTopo, mod.__name__) diff --git a/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py index b3b7256ac4..b701a0d61e 100644 --- a/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py +++ b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py @@ -282,10 +282,26 @@ def test_BGP_config_with_invalid_ASN_p2(request): # Api call to modify AS number input_dict = { - "r1": {"bgp": {"local_as": 0,}}, - "r2": {"bgp": {"local_as": 0,}}, - "r3": {"bgp": {"local_as": 0,}}, - "r4": {"bgp": {"local_as": 64000,}}, + "r1": { + "bgp": { + "local_as": 0, + } + }, + "r2": { + "bgp": { + "local_as": 0, + } + }, + "r3": { + "bgp": { + "local_as": 0, + } + }, + "r4": { + "bgp": { + "local_as": 64000, + } + }, } result = modify_as_number(tgen, topo, input_dict) try: @@ -819,7 +835,11 @@ def test_bgp_with_loopback_interface(request): # Adding ['source_link'] = 'lo' key:value pair topo["routers"][routerN]["bgp"]["address_family"]["ipv4"]["unicast"][ "neighbor" - ][bgp_neighbor]["dest_link"] = {"lo": {"source_link": "lo",}} + ][bgp_neighbor]["dest_link"] = { + "lo": { + "source_link": "lo", + } + } # Creating configuration from JSON build_config_from_json(tgen, topo) diff --git a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py index 54a3c699f3..353df0684b 100644 --- a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py +++ b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py @@ -273,8 +273,20 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): "r3": { "bgp": { "address_family": { - "ipv4": {"unicast": {"maximum_paths": {"ebgp": ecmp_num,}}}, - "ipv6": {"unicast": {"maximum_paths": {"ebgp": ecmp_num,}}}, + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": ecmp_num, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": ecmp_num, + } + } + }, } } } @@ -303,7 +315,7 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): input_dict_1, next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], protocol=protocol, - count_only=True + count_only=True, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( @@ -312,9 +324,9 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): write_test_footer(tc_name) - +@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"]) @pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) -def test_ecmp_after_clear_bgp(request, test_type): +def test_ecmp_after_clear_bgp(request, ecmp_num, test_type): """ Verify BGP table and RIB in DUT after clear BGP routes and neighbors""" tc_name = request.node.name @@ -337,7 +349,7 @@ def test_ecmp_after_clear_bgp(request, test_type): addr_type, dut, input_dict_1, - next_hop=NEXT_HOPS[addr_type], + next_hop=NEXT_HOPS[addr_type][:int(ecmp_num)], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( @@ -360,7 +372,7 @@ def test_ecmp_after_clear_bgp(request, test_type): addr_type, dut, input_dict_1, - next_hop=NEXT_HOPS[addr_type], + next_hop=NEXT_HOPS[addr_type][:int(ecmp_num)], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( @@ -371,8 +383,8 @@ def test_ecmp_after_clear_bgp(request, test_type): def test_ecmp_remove_redistribute_static(request): - """ Verify routes are cleared from BGP and RIB table of DUT when - redistribute static configuration is removed.""" + """Verify routes are cleared from BGP and RIB table of DUT when + redistribute static configuration is removed.""" tc_name = request.node.name write_test_header(tc_name) @@ -481,8 +493,8 @@ def test_ecmp_remove_redistribute_static(request): @pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) def test_ecmp_shut_bgp_neighbor(request, test_type): - """ Shut BGP neigbors one by one and verify BGP and routing table updated - accordingly in DUT """ + """Shut BGP neigbors one by one and verify BGP and routing table updated + accordingly in DUT""" tc_name = request.node.name write_test_header(tc_name) diff --git a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py index 73724ac069..2f73bdb1b8 100644 --- a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py +++ b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py @@ -274,8 +274,20 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): "r3": { "bgp": { "address_family": { - "ipv4": {"unicast": {"maximum_paths": {"ibgp": ecmp_num,}}}, - "ipv6": {"unicast": {"maximum_paths": {"ibgp": ecmp_num,}}}, + "ipv4": { + "unicast": { + "maximum_paths": { + "ibgp": ecmp_num, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ibgp": ecmp_num, + } + } + }, } } } @@ -304,7 +316,7 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): input_dict_1, next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], protocol=protocol, - count_only=True + count_only=True, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( @@ -313,9 +325,9 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): write_test_footer(tc_name) - +@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"]) @pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) -def test_ecmp_after_clear_bgp(request, test_type): +def test_ecmp_after_clear_bgp(request, ecmp_num, test_type): """ Verify BGP table and RIB in DUT after clear BGP routes and neighbors""" tc_name = request.node.name @@ -338,7 +350,7 @@ def test_ecmp_after_clear_bgp(request, test_type): addr_type, dut, input_dict_1, - next_hop=NEXT_HOPS[addr_type], + next_hop=NEXT_HOPS[addr_type][:int(ecmp_num)], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( @@ -361,7 +373,7 @@ def test_ecmp_after_clear_bgp(request, test_type): addr_type, dut, input_dict_1, - next_hop=NEXT_HOPS[addr_type], + next_hop=NEXT_HOPS[addr_type][:int(ecmp_num)], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( @@ -372,8 +384,8 @@ def test_ecmp_after_clear_bgp(request, test_type): def test_ecmp_remove_redistribute_static(request): - """ Verify routes are cleared from BGP and RIB table of DUT when - redistribute static configuration is removed.""" + """Verify routes are cleared from BGP and RIB table of DUT when + redistribute static configuration is removed.""" tc_name = request.node.name write_test_header(tc_name) @@ -482,8 +494,8 @@ def test_ecmp_remove_redistribute_static(request): @pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) def test_ecmp_shut_bgp_neighbor(request, test_type): - """ Shut BGP neigbors one by one and verify BGP and routing table updated - accordingly in DUT """ + """Shut BGP neigbors one by one and verify BGP and routing table updated + accordingly in DUT""" tc_name = request.node.name write_test_header(tc_name) diff --git a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py index 4c56d1a02d..61be947a71 100644 --- a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py +++ b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py @@ -35,6 +35,8 @@ import json import platform from functools import partial +pytestmark = pytest.mark.pimd + # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(CWD, "../")) @@ -658,10 +660,11 @@ def test_evpn_mac(): assertmsg = '"{}" remote MAC content incorrect'.format(tor.name) assert result is None, assertmsg + def check_df_role(dut, esi, role): - ''' + """ Return error string if the df role on the dut is different - ''' + """ es_json = dut.vtysh_cmd("show evpn es %s json" % esi) es = json.loads(es_json) @@ -676,12 +679,13 @@ def check_df_role(dut, esi, role): return None + def test_evpn_df(): - ''' + """ 1. Check the DF role on all the PEs on rack-1. 2. Increase the DF preference on the non-DF and check if it becomes the DF winner. - ''' + """ tgen = get_topogen() @@ -720,10 +724,11 @@ def test_evpn_df(): # tgen.mininet_cli() + def check_protodown_rc(dut, protodown_rc): - ''' + """ check if specified protodown reason code is set - ''' + """ out = dut.vtysh_cmd("show evpn json") @@ -739,13 +744,14 @@ def check_protodown_rc(dut, protodown_rc): return None + def test_evpn_uplink_tracking(): - ''' + """ 1. Wait for access ports to come out of startup-delay 2. disable uplinks and check if access ports have been protodowned 3. enable uplinks and check if access ports have been moved out of protodown - ''' + """ tgen = get_topogen() @@ -778,6 +784,7 @@ def test_evpn_uplink_tracking(): assertmsg = '"{}" protodown rc incorrect'.format(dut_name) assert result is None, assertmsg + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf index c7a76a98ed..991a1e7e56 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf @@ -8,3 +8,4 @@ router bgp 65000 address-family l2vpn evpn neighbor 10.30.30.30 activate advertise-all-vni + advertise-svi-ip diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json index 2937504244..e500a1d85c 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json @@ -6,8 +6,8 @@ "vtepIp":"10.10.10.10", "mcastGroup":"0.0.0.0", "advertiseGatewayMacip":"No", - "numMacs":5, - "numArpNd":3, + "numMacs":6, + "numArpNd":6, "numRemoteVteps":[ "10.30.30.30" ] diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf index 0a24158bb8..52f8687bc1 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf @@ -9,3 +9,4 @@ router bgp 65000 address-family l2vpn evpn neighbor 10.10.10.10 activate advertise-all-vni + advertise-svi-ip diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json index 0853147a00..0a56a235bd 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json @@ -6,8 +6,8 @@ "vtepIp":"10.30.30.30", "mcastGroup":"0.0.0.0", "advertiseGatewayMacip":"No", - "numMacs":5, - "numArpNd":3, + "numMacs":6, + "numArpNd":6, "numRemoteVteps":[ "10.10.10.10" ] diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py index 5098808d55..9a38158b2b 100755 --- a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py +++ b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py @@ -310,8 +310,10 @@ def ip_learn_test(tgen, host, local, remote, ip_addr): assertmsg = "local learned mac wrong type: {} ".format(mac_type) assert mac_type == "local", assertmsg - assertmsg = "learned address mismatch with configured address host: {} learned: {}".format( - ip_addr, learned_ip + assertmsg = ( + "learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) ) assert ip_addr == learned_ip, assertmsg diff --git a/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py index 607b036c6a..a9541a55c5 100644 --- a/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py +++ b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py @@ -238,9 +238,10 @@ def test_next_hop_attribute(request): result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert result is not True, ( - "Testcase {} : Failed \n Error: " - "{} routes are not present in RIB".format(addr_type, tc_name) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "{} routes are not present in RIB".format( + addr_type, tc_name ) # Configure next-hop-self to bgp neighbor diff --git a/tests/topotests/bgp-route-map/test_route_map_topo1.py b/tests/topotests/bgp-route-map/test_route_map_topo1.py index 1aa951edaa..0158e24d31 100644 --- a/tests/topotests/bgp-route-map/test_route_map_topo1.py +++ b/tests/topotests/bgp-route-map/test_route_map_topo1.py @@ -807,7 +807,9 @@ def test_route_map_multiple_seq_different_match_set_clause_p0(request): "prefix_lists": "pf_list_2_{}".format(addr_type) } }, - "set": {"locPrf": 150,}, + "set": { + "locPrf": 150, + }, }, { "action": "permit", @@ -906,7 +908,17 @@ def test_route_map_multiple_seq_different_match_set_clause_p0(request): dut = "r3" protocol = "bgp" input_dict = { - "r3": {"route_maps": {"rmap_match_pf_list1": [{"set": {"metric": 50,}}],}} + "r3": { + "route_maps": { + "rmap_match_pf_list1": [ + { + "set": { + "metric": 50, + } + } + ], + } + } } static_routes = [NETWORK[adt][0]] @@ -1093,7 +1105,14 @@ def test_route_map_set_only_no_match_p0(request): input_dict_4 = { "r3": { "route_maps": { - "rmap_match_pf_1": [{"action": "permit", "set": {"metric": 50,}}] + "rmap_match_pf_1": [ + { + "action": "permit", + "set": { + "metric": 50, + }, + } + ] } } } @@ -1210,7 +1229,13 @@ def test_route_map_match_only_no_set_p0(request): "r1": { "route_maps": { "rmap_match_pf_1_{}".format(addr_type): [ - {"action": "permit", "set": {"metric": 50, "locPrf": 150,}} + { + "action": "permit", + "set": { + "metric": 50, + "locPrf": 150, + }, + } ] } } diff --git a/tests/topotests/bgp-route-map/test_route_map_topo2.py b/tests/topotests/bgp-route-map/test_route_map_topo2.py index 3056aa29f3..958eceba62 100644 --- a/tests/topotests/bgp-route-map/test_route_map_topo2.py +++ b/tests/topotests/bgp-route-map/test_route_map_topo2.py @@ -268,12 +268,20 @@ def test_rmap_match_prefix_list_permit_in_and_outbound_prefixes_p0(): "prefix_lists": { "ipv4": { "pf_list_1_ipv4": [ - {"seqid": 10, "network": "any", "action": "permit",} + { + "seqid": 10, + "network": "any", + "action": "permit", + } ] }, "ipv6": { "pf_list_1_ipv6": [ - {"seqid": 10, "network": "any", "action": "permit",} + { + "seqid": 10, + "network": "any", + "action": "permit", + } ] }, } @@ -472,7 +480,11 @@ def test_modify_set_match_clauses_in_rmap_p0(): "prefix_lists": { "ipv4": { "pf_list_1_ipv4": [ - {"seqid": 10, "network": "any", "action": "permit",} + { + "seqid": 10, + "network": "any", + "action": "permit", + } ], "pf_list_2_ipv4": [ {"seqid": 10, "network": "any", "action": "permit"} @@ -480,7 +492,11 @@ def test_modify_set_match_clauses_in_rmap_p0(): }, "ipv6": { "pf_list_1_ipv6": [ - {"seqid": 10, "network": "any", "action": "permit",} + { + "seqid": 10, + "network": "any", + "action": "permit", + } ], "pf_list_2_ipv6": [ {"seqid": 10, "network": "any", "action": "permit"} @@ -506,7 +522,9 @@ def test_modify_set_match_clauses_in_rmap_p0(): "prefix_lists": "pf_list_1_{}".format(addr_type) } }, - "set": {"locPrf": 150,}, + "set": { + "locPrf": 150, + }, } ], "rmap_match_pf_2_{}".format(addr_type): [ @@ -666,7 +684,9 @@ def test_modify_set_match_clauses_in_rmap_p0(): "prefix_lists": "pf_list_1_{}".format(addr_type) } }, - "set": {"locPrf": 1000,}, + "set": { + "locPrf": 1000, + }, } ], "rmap_match_pf_2_{}".format(addr_type): [ @@ -816,12 +836,20 @@ def test_modify_prefix_list_referenced_by_rmap_p0(): "prefix_lists": { "ipv4": { "pf_list_1_ipv4": [ - {"seqid": 10, "network": "any", "action": "permit",} + { + "seqid": 10, + "network": "any", + "action": "permit", + } ] }, "ipv6": { "pf_list_1_ipv6": [ - {"seqid": 100, "network": "any", "action": "permit",} + { + "seqid": 100, + "network": "any", + "action": "permit", + } ] }, } @@ -1090,7 +1118,9 @@ def test_remove_prefix_list_referenced_by_rmap_p0(): "prefix_lists": "pf_list_1_{}".format(addr_type) } }, - "set": {"locPrf": 150,}, + "set": { + "locPrf": 150, + }, } ], "rmap_match_pf_2_{}".format(addr_type): [ @@ -1894,7 +1924,9 @@ def test_multiple_match_statement_in_route_map_logical_ANDed_p1(): "prefix_lists": "pf_list_1_{}".format(addr_type) } }, - "set": {"locPrf": 150,}, + "set": { + "locPrf": 150, + }, } ] } @@ -1921,7 +1953,9 @@ def test_multiple_match_statement_in_route_map_logical_ANDed_p1(): } } }, - "set": {"locPrf": 150,}, + "set": { + "locPrf": 150, + }, } ] } @@ -2048,7 +2082,9 @@ def test_add_remove_rmap_to_specific_neighbor_p0(): "prefix_lists": "pf_list_1_{}".format(addr_type) } }, - "set": {"locPrf": 150,}, + "set": { + "locPrf": 150, + }, } ] } @@ -3505,7 +3541,9 @@ def test_create_rmap_match_prefix_list_to_deny_in_and_outbound_prefixes_p0(): "prefix_lists": "pf_list_1_{}".format(addr_type) } }, - "set": {"locPrf": 150,}, + "set": { + "locPrf": 150, + }, } ], "rmap_match_pf_2_{}".format(addr_type): [ diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce1/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/bgpd.conf new file mode 100644 index 0000000000..b598666dfb --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + timers bgp 3 9 + bgp router-id 192.168.100.10 + neighbor 192.168.100.20 remote-as 65001 + neighbor 192.168.100.20 update-source 192.168.100.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce1/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/snmpd.conf new file mode 100644 index 0000000000..36218d3538 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.5.5.5:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce1/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/zebra.conf new file mode 100644 index 0000000000..8ad2ddc48c --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface ce1-eth0 + ip address 192.168.100.10/24 + ipv6 address 2000:1:1:100::10/64 +! +! +interface lo + ip address 10.5.5.5/32 + ipv6 address 2000:5:5:5::5/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce2/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/bgpd.conf new file mode 100644 index 0000000000..e388ccba8a --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + bgp router-id 192.168.200.10 + timers bgp 3 9 + neighbor 192.168.200.20 remote-as 65001 + neighbor 192.168.200.20 update-source 192.168.200.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce2/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/snmpd.conf new file mode 100644 index 0000000000..714585cb9b --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.6.6.6:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce2/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/zebra.conf new file mode 100644 index 0000000000..fa2e968e55 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface ce2-eth0 + ip address 192.168.200.10/24 + ipv6 address 2000:1:1:200::10/64 +! +! +interface lo + ip address 10.6.6.6/32 + ipv6 address 2000:6:6:6::6/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce3/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/bgpd.conf new file mode 100644 index 0000000000..e388ccba8a --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + bgp router-id 192.168.200.10 + timers bgp 3 9 + neighbor 192.168.200.20 remote-as 65001 + neighbor 192.168.200.20 update-source 192.168.200.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce3/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/snmpd.conf new file mode 100644 index 0000000000..36218d3538 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.5.5.5:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce3/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/zebra.conf new file mode 100644 index 0000000000..ea91e21bad --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface ce3-eth0 + ip address 192.168.200.10/24 + ipv6 address 2000:1:1:200::10/64 +! +! +interface lo + ip address 10.7.7.7/32 + ipv6 address 2000:7:7:7::7/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce4/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/bgpd.conf new file mode 100644 index 0000000000..e388ccba8a --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + bgp router-id 192.168.200.10 + timers bgp 3 9 + neighbor 192.168.200.20 remote-as 65001 + neighbor 192.168.200.20 update-source 192.168.200.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce4/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/snmpd.conf new file mode 100644 index 0000000000..36218d3538 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.5.5.5:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce4/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/zebra.conf new file mode 100644 index 0000000000..0866fa9759 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface ce4-eth0 + ip address 192.168.34.10/24 + ipv6 address 2000:1:1:300::10/64 +! +! +interface lo + ip address 10.8.8.8/32 + ipv6 address 2000:8:8:8::8/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r1/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r1/bgpd.conf new file mode 100644 index 0000000000..098e55d0ed --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r1/bgpd.conf @@ -0,0 +1,48 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65000 + bgp router-id 10.1.1.1 + neighbor 10.4.4.4 remote-as 65000 + neighbor 10.4.4.4 update-source 10.1.1.1 + neighbor 10.4.4.4 timers connect 10 + ! + address-family ipv4 vpn + neighbor 10.4.4.4 activate + exit-address-family + +! +router bgp 65001 vrf VRF-a + bgp router-id 192.168.100.20 + timers bgp 3 9 + neighbor 192.168.100.10 remote-as 65001 + neighbor 192.168.100.10 update-source 192.168.100.20 + neighbor 192.168.200.10 remote-as 65001 + neighbor 192.168.200.10 update-source 192.168.200.20 + ! + address-family ipv4 unicast + redistribute connected + redistribute isis + label vpn export 1111 + rd vpn export 10:1 + rt vpn both 1:1 + export vpn + import vpn + exit-address-family + +router bgp 65002 vrf VRF-b + bgp router-id 192.168.10.20 + timers bgp 3 9 + neighbor 192.168.10.10 remote-as 65003 + neighbor 192.168.10.10 update-source 192.168.10.20 +! + address-family ipv4 unicast + redistribute connected + redistribute isis + label vpn export 6666 + rd vpn export 10:2 + rt vpn both 1:2 + export vpn + import vpn + exit-address-family + +agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r1/isisd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r1/isisd.conf new file mode 100644 index 0000000000..b5ca993da3 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r1/isisd.conf @@ -0,0 +1,46 @@ +log stdout debugging +! +debug isis route-events +debug isis events +! +interface r1-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r1-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r1-eth2 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0001.00 + is-type level-1 + topology ipv6-unicast +! +line vty +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r1/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r1/snmpd.conf new file mode 100644 index 0000000000..c903c1ad2e --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r1/snmpd.conf @@ -0,0 +1,17 @@ +agentAddress udp:10.1.1.1:161 + +com2sec public 10.1.1.1 public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx + +noRangeCheck yes
\ No newline at end of file diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r1/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r1/zebra.conf new file mode 100644 index 0000000000..7228ae6bd2 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r1/zebra.conf @@ -0,0 +1,33 @@ +log file zebra.log +! +interface r1-eth0 + ip address 192.168.12.12/24 + ipv6 address 2000:1:1:12::12/64 +! +interface r1-eth1 + ip address 192.168.13.13/24 + ipv6 address 2000:1:1:13::13/64 +! +interface r1-eth2 + ip address 192.168.14.14/24 + ipv6 address 2000:1:1:14::14/64 +! +interface r1-eth3 vrf VRF-a + ip address 192.168.100.20/24 + ipv6 address 2000:1:1:100::20/64 +! +interface r1-eth4 vrf VRF-a + ip address 192.168.200.20/24 + ipv6 address 2000:1:1:200::20/64 +! +interface r1-eth5 vrf VRF-b + ip address 192.168.300.20/24 + ipv6 address 2000:1:1:300::20/64 + +interface lo + ip address 10.1.1.1/32 + ipv6 address 2000:1:1:1::1/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r2/isisd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r2/isisd.conf new file mode 100644 index 0000000000..3dfa43831a --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r2/isisd.conf @@ -0,0 +1,37 @@ +log stdout debugging +! +debug isis route-events +debug isis events +! +interface r2-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r2-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0002.00 + is-type level-1 + topology ipv6-unicast +! +line vty +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r2/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r2/snmpd.conf new file mode 100644 index 0000000000..0cfebc7238 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r2/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.2.2.2:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r2/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r2/zebra.conf new file mode 100644 index 0000000000..9bc4331bae --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r2/zebra.conf @@ -0,0 +1,24 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface r2-eth0 + ip address 192.168.12.21/24 + ipv6 address 2000:1:1:12::21/64 +! +interface r2-eth1 + ip address 192.168.23.23/24 + ipv6 address 2000:1:1:23::23/64 +! +! +interface lo + ip address 10.2.2.2/32 + ipv6 address 2000:2:2:2::2/128 +! +! +! +line vty +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r3/isisd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r3/isisd.conf new file mode 100644 index 0000000000..578ebafad6 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r3/isisd.conf @@ -0,0 +1,45 @@ +log stdout debugging +! +debug isis route-events +debug isis events +! +interface r3-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r3-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r3-eth2 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0003.00 + is-type level-1 + topology ipv6-unicast +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r3/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r3/snmpd.conf new file mode 100644 index 0000000000..b9eb00ea52 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r3/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.3.3.3:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r3/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r3/zebra.conf new file mode 100644 index 0000000000..4d2007e787 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r3/zebra.conf @@ -0,0 +1,27 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface r3-eth0 + ip address 192.168.13.31/24 + ipv6 address 2000:1:1:13::31/64 +! +interface r3-eth1 + ip address 192.168.23.32/24 + ipv6 address 2000:1:1:23::32/64 +! +interface r3-eth2 + ip address 192.168.34.34/24 + ipv6 address 2000:1:1:34::34/64 +! +! +interface lo + ip address 10.3.3.3/32 + ipv6 address 2000:3:3:3::3/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r4/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r4/bgpd.conf new file mode 100644 index 0000000000..2a834c799e --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r4/bgpd.conf @@ -0,0 +1,43 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65000 + bgp router-id 10.4.4.4 + timers bgp 3 9 + neighbor 10.1.1.1 remote-as 65000 + neighbor 10.1.1.1 update-source 10.4.4.4 + neighbor 10.1.1.1 timers connect 10 + ! + address-family ipv4 vpn + neighbor 10.1.1.1 activate + exit-address-family +! + + address-family ipv6 vpn + neighbor 10.1.1.1 activate + exit-address-family +! +router bgp 65001 vrf VRF-a + bgp router-id 192.168.200.20 + timers bgp 3 9 + neighbor 192.168.200.10 remote-as 65001 + neighbor 192.168.200.10 update-source 192.168.200.20 + ! + address-family ipv4 unicast + redistribute connected + redistribute isis + label vpn export 1111 + rd vpn export 10:3 + rt vpn both 1:1 + export vpn + import vpn + exit-address-family + + address-family ipv6 unicast + redistribute connected + redistribute isis + label vpn export 1111 + rd vpn export 10:3 + rt vpn both 1:2 + export vpn + import vpn + exit-address-family diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r4/isisd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r4/isisd.conf new file mode 100644 index 0000000000..3e9e9af45f --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r4/isisd.conf @@ -0,0 +1,36 @@ +log stdout debugging +! +debug isis route-events +debug isis events +! +interface r4-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r4-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0004.00 + is-type level-1 + topology ipv6-unicast +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r4/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r4/snmpd.conf new file mode 100644 index 0000000000..ec35f9f9c9 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r4/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.4.4.4:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r4/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r4/zebra.conf new file mode 100644 index 0000000000..c48407c108 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r4/zebra.conf @@ -0,0 +1,27 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface r4-eth0 + ip address 192.168.14.41/24 + ipv6 address 2000:1:1:14::41/64 +! +interface r4-eth1 + ip address 192.168.34.43/24 + ipv6 address 2000:1:1:34::43/64 +! +interface r4-eth2 vrf aaa + ip address 192.168.200.20/24 + ipv6 address 2000:1:1:200::20/64 +! +! +interface lo + ip address 10.4.4.4/32 + ipv6 address 2000:4:4:4::4/128 +! +! +line vty +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py b/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py new file mode 100755 index 0000000000..5eb1738632 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py @@ -0,0 +1,738 @@ +#!/usr/bin/env python + +# +# test_bgp_snmp_mplsl3vpn.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# 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_bgp_snmp_mplsl3vpn.py: Test mplsL3Vpn MIB [RFC4382]. +""" + +import os +import sys +import json +from functools import partial +from time import sleep +import pytest +import re + +# 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 +from lib.snmptest import SnmpTester + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + tgen.add_router("r4") + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("ce3") + tgen.add_router("ce4") + + # r1-r2 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # r1-r3 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + # r1-r4 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r4"]) + + # r1-ce1 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ce1"]) + + # r1-ce3 + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ce3"]) + + # r1-ce4 + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ce4"]) + + # r1-dangling + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["r1"]) + + # r2-r3 + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + # r3-r4 + switch = tgen.add_switch("s9") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + # r4-ce2 + switch = tgen.add_switch("s10") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["ce2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + # skip tests is SNMP not installed + snmpd = os.system("which snmpd") + if snmpd: + error_msg = "SNMP not installed - skipping" + pytest.skip(error_msg) + + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] + + # setup VRF-a in r1 + r1.run("ip link add VRF-a type vrf table 1001") + r1.run("ip link set up dev VRF-a") + r1.run("ip link add VRF-b type vrf table 1002") + r1.run("ip link set up dev VRF-b") + r4.run("ip link add VRF-a type vrf table 1001") + r4.run("ip link set up dev VRF-a") + + # enslave vrf interfaces + r1.run("ip link set r1-eth3 master VRF-a") + r1.run("ip link set r1-eth4 master VRF-a") + r1.run("ip link set r1-eth5 master VRF-b") + r4.run("ip link set r4-eth1 master VRF-a") + + r1.run("sysctl -w net.ipv4.ip_forward=1") + r2.run("sysctl -w net.ipv4.ip_forward=1") + r3.run("sysctl -w net.ipv4.ip_forward=1") + r4.run("sysctl -w net.ipv4.ip_forward=1") + r1.run("sysctl -w net.mpls.conf.r1-eth0.input=1") + r1.run("sysctl -w net.mpls.conf.r1-eth1.input=1") + r1.run("sysctl -w net.mpls.conf.r1-eth2.input=1") + r2.run("sysctl -w net.mpls.conf.r1-eth0.input=1") + r2.run("sysctl -w net.mpls.conf.r1-eth1.input=1") + r3.run("sysctl -w net.mpls.conf.r1-eth0.input=1") + r3.run("sysctl -w net.mpls.conf.r1-eth1.input=1") + r3.run("sysctl -w net.mpls.conf.r1-eth2.input=1") + r4.run("sysctl -w net.mpls.conf.r1-eth0.input=1") + r4.run("sysctl -w net.mpls.conf.r1-eth1.input=1") + + router_list = tgen.routers() + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, "{}/bgpd.conf".format(rname)), + "-M snmp", + ) + router.load_config( + TopoRouter.RD_SNMP, + os.path.join(CWD, "{}/snmpd.conf".format(rname)), + "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap", + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +# SNMP utilities - maybe move to lib +def snmp_uint32_to_oid(val): + oid1 = int(val / 16777216) % 256 + oid2 = int(val / 65536) % 256 + oid3 = int(val / 256) % 256 + oid4 = int(val) % 256 + return "%(oid1)s.%(oid2)s.%(oid3)s.%(oid4)s" % locals() + + +def snmp_oid_to_uint32(oid): + values = oid.split(".") + return ( + (int(values[0]) * 16777216) + + (int(values[1]) * 65536) + + (int(values[2]) * 256) + + int(values[3]) + ) + + +def snmp_str_to_oid(str): + out_oid = "" + for char in str: + out_oid += "{}.".format(ord(char)) + return out_oid.rstrip(".") + + +def snmp_oid_to_str(oid): + out_str = "" + oids = oid.split(".") + for char in oids: + out_str += "{}".format(chr(int(char))) + return out_str + + +def snmp_rte_oid(vrf, dtype, dest, plen, policy, ntype, nhop=0): + oid_1 = snmp_str_to_oid(vrf) + oid_2 = dtype + oid_3 = dest + oid_4 = plen + oid_5 = "0.{}".format(policy) + oid_6 = ntype + if ntype == 0: + oid_7 = "" + else: + oid_7 = ".{}".format(nhop) + + return "{}.{}.{}.{}.{}.{}{}".format(oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7) + + +def test_pe1_converge_evpn(): + "Wait for protocol convergence" + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + assertmsg = "BGP SNMP does not seem to be running" + assert r1_snmp.test_oid("bgpVersion", "10"), assertmsg + count = 0 + passed = False + while count < 125: + if r1_snmp.test_oid_walk("bgpPeerLocalAddr.10.4.4.4", ["10.1.1.1"]): + passed = True + break + count += 1 + sleep(1) + #tgen.mininet_cli() + assertmsg = "BGP Peer 10.4.4.4 did not connect" + assert passed, assertmsg + + +interfaces_up_test = { + "mplsL3VpnConfiguredVrfs": "2", + "mplsL3VpnActiveVrfs": "2", + "mplsL3VpnConnectedInterfaces": "3", + "mplsL3VpnNotificationEnable": "true(1)", + "mplsL3VpnVrfConfMaxPossRts": "0", + "mplsL3VpnVrfConfRteMxThrshTime": "0 seconds", + "mplsL3VpnIlllblRcvThrsh": "0", +} + +interfaces_down_test = { + "mplsL3VpnConfiguredVrfs": "2", + "mplsL3VpnActiveVrfs": "1", + "mplsL3VpnConnectedInterfaces": "3", + "mplsL3VpnNotificationEnable": "true(1)", + "mplsL3VpnVrfConfMaxPossRts": "0", + "mplsL3VpnVrfConfRteMxThrshTime": "0 seconds", + "mplsL3VpnIlllblRcvThrsh": "0", +} + + +def test_r1_mplsvpn_scalars(): + "check scalar values" + tgen = get_topogen() + r1 = tgen.net.get("r1") + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + for item in interfaces_up_test.keys(): + assertmsg = "{} should be {}: value {}".format( + item, interfaces_up_test[item], r1_snmp.get_next(item) + ) + assert r1_snmp.test_oid(item, interfaces_up_test[item]), assertmsg + + +def test_r1_mplsvpn_scalars_interface(): + "check scalar interface changing values" + tgen = get_topogen() + r1 = tgen.net.get("r1") + r1_cmd = tgen.gears["r1"] + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + r1_cmd.vtysh_cmd("conf t\ninterface r1-eth3\nshutdown") + r1_cmd.vtysh_cmd("conf t\ninterface r1-eth4\nshutdown") + + for item in interfaces_up_test.keys(): + assertmsg = "{} should be {}: value {}".format( + item, interfaces_down_test[item], r1_snmp.get_next(item) + ) + assert r1_snmp.test_oid(item, interfaces_down_test[item]), assertmsg + + r1_cmd.vtysh_cmd("conf t\ninterface r1-eth3\nno shutdown") + r1_cmd.vtysh_cmd("conf t\ninterface r1-eth4\nno shutdown") + + for item in interfaces_up_test.keys(): + assertmsg = "{} should be {}: value {}".format( + item, interfaces_up_test[item], r1_snmp.get_next(item) + ) + assert r1_snmp.test_oid(item, interfaces_up_test[item]), assertmsg + + +def router_interface_get_ifindex(router, interface): + ifindex = 0 + r_int_output = router.vtysh_cmd( + "show interface {}-{}".format(router.name, interface) + ) + int_lines = r_int_output.splitlines() + for line in int_lines: + line_items = line.lstrip().split(" ") + if "index" in line_items[0]: + ifindex = line_items[1] + return ifindex + + +def generate_vrf_ifindex_oid(vrf, ifindex): + + intoid = snmp_uint32_to_oid(int(ifindex)) + vrfoid = snmp_str_to_oid(vrf) + oid = "{}.{}".format(vrfoid, intoid) + + return oid + + +def generate_vrf_index_type_oid(vrf, index, type): + vrfoid = snmp_str_to_oid(vrf) + intoid = snmp_uint32_to_oid(int(index)) + oid = "{}.{}.{}".format(vrfoid, intoid, type) + + return oid + + +iftable_up_test = { + "mplsL3VpnIfVpnClassification": ["enterprise(2)", "enterprise(2)", "enterprise(2)"], + "mplsL3VpnIfConfStorageType": ["volatile(2)", "volatile(2)", "volatile(2)"], + "mplsL3VpnIfConfRowStatus": ["active(1)", "active(1)", "active(1)"], +} + + +def get_timetick_val(time): + return int(time.split(" ")[0].lstrip("(").rstrip(")")) + + +def test_r1_mplsvpn_IfTable(): + "mplsL3VpnIf table values" + + tgen = get_topogen() + r1 = tgen.net.get("r1") + r1r = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + eth3_ifindex = router_interface_get_ifindex(r1r, "eth3") + eth4_ifindex = router_interface_get_ifindex(r1r, "eth4") + eth5_ifindex = router_interface_get_ifindex(r1r, "eth5") + + # get ifindex and make sure the oid is correct + + oids = [] + # generate oid + oids.append(generate_vrf_ifindex_oid("VRF-a", eth3_ifindex)) + oids.append(generate_vrf_ifindex_oid("VRF-a", eth4_ifindex)) + oids.append(generate_vrf_ifindex_oid("VRF-b", eth5_ifindex)) + + for item in iftable_up_test.keys(): + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, iftable_up_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, iftable_up_test[item], oids), assertmsg + + # an inactive vrf should not affect these values + r1.cmd("ip link set r1-eth5 down") + + for item in iftable_up_test.keys(): + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, iftable_up_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, iftable_up_test[item], oids), assertmsg + + r1.cmd("ip link set r1-eth5 up") + + +vrftable_test = { + "mplsL3VpnVrfDescription": ["VRF-a", "VRF-b"], + "mplsL3VpnVrfRD": ['"10:1"', '"10:2"'], + "mplsL3VpnVrfOperStatus": ["up(1)", "up(1)"], + "mplsL3VpnVrfActiveInterfaces": ["2", "1"], + "mplsL3VpnVrfAssociatedInterfaces": ["2", "1"], + "mplsL3VpnVrfConfMidRteThresh": ["0", "0"], + "mplsL3VpnVrfConfHighRteThresh": ["0", "0"], + "mplsL3VpnVrfConfMaxRoutes": ["0", "0"], + "mplsL3VpnVrfConfRowStatus": ["active(1)", "active(1)"], + "mplsL3VpnVrfConfAdminStatus": ["up(1)", "up(1)"], + "mplsL3VpnVrfConfStorageType": ["volatile(2)", "volatile(2)"], +} + + +def test_r1_mplsvpn_VrfTable(): + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1r = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + + oids = [] + + oids.append(snmp_str_to_oid("VRF-a")) + oids.append(snmp_str_to_oid("VRF-b")) + + # check items + for item in vrftable_test.keys(): + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, vrftable_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, vrftable_test[item], oids), assertmsg + + # check timetick set and stable + ts_a = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-a"))) + ts_b = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-b"))) + ts_val_a1 = get_timetick_val(ts_a) + ts_val_b1 = get_timetick_val(ts_b) + ts_a = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-a"))) + ts_b = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-b"))) + ts_val_a2 = get_timetick_val(ts_a) + ts_val_b2 = get_timetick_val(ts_b) + + assertmsg = "timestamp values for VRF-a do not match {} {}".format( + ts_val_a1, ts_val_a2 + ) + assert ts_val_a1 == ts_val_a2, assertmsg + assertmsg = "timestamp values for VRF-b do not match {} {}".format( + ts_val_b1, ts_val_b2 + ) + assert ts_val_b1 == ts_val_b2, assertmsg + + # take Last changed time, fiddle with active interfaces, ensure + # time changes and active interfaces change + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_1 = get_timetick_val(ts_last) + r1r.vtysh_cmd("conf t\ninterface r1-eth3\nshutdown") + active_int = r1_snmp.get( + "mplsL3VpnVrfActiveInterfaces.{}".format(snmp_str_to_oid("VRF-a")) + ) + assertmsg = "mplsL3VpnVrfActiveInterfaces incorrect should be 1 value {}".format( + active_int + ) + assert active_int == "1", assertmsg + + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_2 = get_timetick_val(ts_last) + assertmsg = "mplsL3VpnVrfConfLastChanged does not update on interface change" + assert ts_val_last_2 > ts_val_last_1, assertmsg + r1r.vtysh_cmd("conf t\ninterface r1-eth3\nno shutdown") + + # take Last changed time, fiddle with associated interfaces, ensure + # time changes and active interfaces change + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_1 = get_timetick_val(ts_last) + r1.cmd("ip link set r1-eth6 master VRF-a") + r1.cmd("ip link set r1-eth6 up") + + associated_int = r1_snmp.get( + "mplsL3VpnVrfAssociatedInterfaces.{}".format(snmp_str_to_oid("VRF-a")) + ) + assertmsg = "mplsL3VpnVrfAssociatedInterfaces incorrect should be 3 value {}".format( + associated_int + ) + + assert associated_int == "3", assertmsg + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_2 = get_timetick_val(ts_last) + assertmsg = "mplsL3VpnVrfConfLastChanged does not update on interface change" + assert ts_val_last_2 > ts_val_last_1, assertmsg + r1.cmd("ip link del r1-eth6 master VRF-a") + r1.cmd("ip link set r1-eth6 down") + + +rt_table_test = { + "mplsL3VpnVrfRT": ['"1:1"', '"1:2"'], + "mplsL3VpnVrfRTDescr": ["RT both for VRF VRF-a", "RT both for VRF VRF-b"], + "mplsL3VpnVrfRTRowStatus": ["active(1)", "active(1)"], + "mplsL3VpnVrfRTStorageType": ["volatile(2)", "volatile(2)"], +} + + +def test_r1_mplsvpn_VrfRT_table(): + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1r = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + oids = [] + oids.append(generate_vrf_index_type_oid("VRF-a", 1, 3)) + oids.append(generate_vrf_index_type_oid("VRF-b", 1, 3)) + + # check items + for item in rt_table_test.keys(): + print(item) + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, rt_table_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, rt_table_test[item], oids), assertmsg + + +def test_r1_mplsvpn_perf_table(): + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1r = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + oid_a = snmp_str_to_oid("VRF-a") + oid_b = snmp_str_to_oid("VRF-b") + + # poll for 10 seconds for routes to appear + count = 0 + passed = False + while count < 60: + if r1_snmp.test_oid_walk( + "mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a), ["7"] + ): + passed = True + break + count += 1 + sleep(1) + # tgen.mininet_cli() + assertmsg = "mplsL3VpnVrfPerfCurrNumRoutes shouold be 7 got {}".format( + r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a)) + ) + assert passed, assertmsg + curr_a = int(r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a))) + del_a = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesDeleted.{}".format(oid_a))) + add_a = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesAdded.{}".format(oid_a))) + + assertmsg = "FAIL curr{} does not equal added{} - deleted {}".format( + curr_a, add_a, del_a + ) + assert curr_a == (add_a - del_a), assertmsg + curr_b = int(r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_b))) + del_b = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesDeleted.{}".format(oid_b))) + add_b = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesAdded.{}".format(oid_b))) + assertmsg = "FAIL curr{} does not equal added{} - deleted {}".format( + curr_b, add_b, del_b + ) + assert curr_b == (add_b - del_b), assertmsg + + +rte_table_test = { + "mplsL3VpnVrfRteInetCidrDestType": [ + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + ], + "mplsL3VpnVrfRteInetCidrDest": [ + "0A 05 05 05", + "0A 07 07 07", + "C0 A8 22 00", + "C0 A8 64 00", + "C0 A8 64 00", + "C0 A8 C8 00", + "C0 A8 C8 00", + ], + "mplsL3VpnVrfRteInetCidrPfxLen": ["32", "32", "24", "24", "24", "24", "24"], + "mplsL3VpnVrfRteInetCidrNHopType": [ + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "unknown(0)", + "ipv4(1)", + "unknown(0)", + ], + "mplsL3VpnVrfRteInetCidrNextHop": [ + "C0 A8 64 0A", + "C0 A8 C8 0A", + "0A 04 04 04", + "C0 A8 64 0A", + '""', + "C0 A8 C8 0A", + '""', + ], + "mplsL3VpnVrfRteInetCidrIfIndex": ["5", "6", "4", "5", "0", "6", "0"], + "mplsL3VpnVrfRteInetCidrType": [ + "local(3)", + "local(3)", + "remote(4)", + "local(3)", + "other(1)", + "local(3)", + "other(1)", + ], + "mplsL3VpnVrfRteInetCidrProto": [ + "bgp(14)", + "bgp(14)", + "bgp(14)", + "bgp(14)", + "local(2)", + "bgp(14)", + "local(2)", + ], + "mplsL3VpnVrfRteInetCidrNextHopAS": ["65001", "65001", "0", "65001", "0", "65001", "0"], + "mplsL3VpnVrfRteInetCidrMetric1": ["0", "0", "20", "0", "0", "0", "0"], + "mplsL3VpnVrfRteInetCidrMetric2": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteInetCidrMetric3": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteInetCidrMetric4": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteInetCidrMetric5": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteXCPointer": ["00", "00", "00", "00", "00", "00", "00"], + "mplsL3VpnVrfRteInetCidrStatus": [ + "active(1)", + "active(1)", + "active(1)", + "active(1)", + "active(1)", + "active(1)", + "active(1)", + ], +} + + +def test_r1_mplsvpn_rte_table(): + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1r = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + oid_1 = snmp_rte_oid("VRF-a", 1, "10.5.5.5", 32, 0, 1, "192.168.100.10") + oid_2 = snmp_rte_oid("VRF-a", 1, "10.7.7.7", 32, 0, 1, "192.168.200.10") + oid_3 = snmp_rte_oid("VRF-a", 1, "192.168.34.0", 24, 0, 1, "10.4.4.4") + oid_4 = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 1, 1, "192.168.100.10") + oid_4_a = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 0, 1, "192.168.100.10") + oid_5 = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 0, 0) + oid_5_a = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 1, 0) + oid_6 = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 1, 1, "192.168.200.10") + oid_6_a = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 0, 1, "192.168.200.10") + oid_7 = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 0, 0) + oid_7_a = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 1, 0) + + oid_lists = [ + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6_a, oid_7_a], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6_a, oid_7_a], + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6_a, oid_7_a], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6_a, oid_7_a], + ] + + # check items + + passed = False + for oid_list in oid_lists: + passed = True + for item in rte_table_test.keys(): + print(item) + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, rte_table_test[item], oid_list, r1_snmp.walk(item) + ) + if not r1_snmp.test_oid_walk(item, rte_table_test[item], oid_list): + passed = False + break + print( + "{} should be {} oids {} full dict {}:".format( + item, rte_table_test[item], oid_list, r1_snmp.walk(item) + ) + ) + if passed: + break + print("passed {}".format(passed)) + assert passed, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py b/tests/topotests/bgp-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py index 36f1d8cd56..71f64e9b70 100644 --- a/tests/topotests/bgp-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py +++ b/tests/topotests/bgp-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py @@ -90,7 +90,11 @@ def test_vrf_route_leak(): # Test DONNA VRF. expect = { - "10.0.0.0/24": [{"protocol": "connected",}], + "10.0.0.0/24": [ + { + "protocol": "connected", + } + ], "10.0.1.0/24": [ {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} ], @@ -111,11 +115,19 @@ def test_vrf_route_leak(): "10.0.0.0/24": [ {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} ], - "10.0.1.0/24": [{"protocol": "connected",}], + "10.0.1.0/24": [ + { + "protocol": "connected", + } + ], "10.0.2.0/24": [ {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} ], - "10.0.3.0/24": [{"protocol": "connected",}], + "10.0.3.0/24": [ + { + "protocol": "connected", + } + ], } test_func = partial( diff --git a/tests/topotests/bgp_blackhole_community/__init__.py b/tests/topotests/bgp_blackhole_community/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/__init__.py diff --git a/tests/topotests/bgp_blackhole_community/r1/bgpd.conf b/tests/topotests/bgp_blackhole_community/r1/bgpd.conf new file mode 100644 index 0000000000..574d199aeb --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r1/bgpd.conf @@ -0,0 +1,14 @@ +! +router bgp 65001 + timers bgp 3 9 + no bgp ebgp-requires-policy + neighbor r1-eth0 interface remote-as external + address-family ipv4 unicast + redistribute connected + neighbor r1-eth0 route-map r2 out + exit-address-family + ! +! +route-map r2 permit 10 + set community blackhole +! diff --git a/tests/topotests/bgp_blackhole_community/r1/zebra.conf b/tests/topotests/bgp_blackhole_community/r1/zebra.conf new file mode 100644 index 0000000000..70dc5e516d --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r1/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! +ip forwarding +! + diff --git a/tests/topotests/bgp_blackhole_community/r2/bgpd.conf b/tests/topotests/bgp_blackhole_community/r2/bgpd.conf new file mode 100644 index 0000000000..2260613253 --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r2/bgpd.conf @@ -0,0 +1,8 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor r2-eth0 interface remote-as external + neighbor r2-eth1 interface remote-as external + neighbor r2-eth2 interface remote-as internal +! diff --git a/tests/topotests/bgp_blackhole_community/r2/zebra.conf b/tests/topotests/bgp_blackhole_community/r2/zebra.conf new file mode 100644 index 0000000000..cf6fb6d984 --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r2/zebra.conf @@ -0,0 +1,12 @@ +! +interface r2-eth0 + ip address 192.168.0.2/24 +! +interface r2-eth1 + ip address 192.168.1.1/24 +! +interface r2-eth2 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_blackhole_community/r3/bgpd.conf b/tests/topotests/bgp_blackhole_community/r3/bgpd.conf new file mode 100644 index 0000000000..d0c4b400f7 --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r3/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65003 + timers bgp 3 9 + no bgp ebgp-requires-policy + neighbor r3-eth0 interface remote-as external +! diff --git a/tests/topotests/bgp_blackhole_community/r3/zebra.conf b/tests/topotests/bgp_blackhole_community/r3/zebra.conf new file mode 100644 index 0000000000..05ab56d6f1 --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.1.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_blackhole_community/r4/bgpd.conf b/tests/topotests/bgp_blackhole_community/r4/bgpd.conf new file mode 100644 index 0000000000..0ac963e642 --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r4/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65002 + timers bgp 3 9 + no bgp ebgp-requires-policy + neighbor r4-eth0 interface remote-as internal +! diff --git a/tests/topotests/bgp_blackhole_community/r4/zebra.conf b/tests/topotests/bgp_blackhole_community/r4/zebra.conf new file mode 100644 index 0000000000..e2ccaed52a --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.2.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py b/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py new file mode 100644 index 0000000000..a856c9278f --- /dev/null +++ b/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# 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 if 172.16.255.254/32 tagged with BLACKHOLE community is not +re-advertised downstream outside local AS. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo +from lib.common_config import step + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_blackhole_community(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(): + output = json.loads( + tgen.gears["r2"].vtysh_cmd("show ip bgp 172.16.255.254/32 json") + ) + expected = {"paths": [{"community": {"list": ["blackhole", "noExport"]}}]} + return topotest.json_cmp(output, expected) + + def _bgp_no_advertise_ebgp(): + output = json.loads( + tgen.gears["r2"].vtysh_cmd( + "show ip bgp neighbor r2-eth1 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": {}, + "totalPrefixCounter": 0, + "filteredPrefixCounter": 0, + } + + return topotest.json_cmp(output, expected) + + def _bgp_no_advertise_ibgp(): + output = json.loads( + tgen.gears["r2"].vtysh_cmd( + "show ip bgp neighbor r2-eth2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": {"172.16.255.254/32": {}}, + "totalPrefixCounter": 2, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r2"]) + + step("Check if 172.16.255.254/32 is not advertised to eBGP peers") + + test_func = functools.partial(_bgp_no_advertise_ebgp) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert ( + result is None + ), 'Advertised blackhole tagged prefix to eBGP peers in "{}"'.format( + tgen.gears["r2"] + ) + + step("Check if 172.16.255.254/32 is advertised to iBGP peers") + + test_func = functools.partial(_bgp_no_advertise_ibgp) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert ( + result is None + ), 'Withdrawn blackhole tagged prefix to iBGP peers in "{}"'.format( + tgen.gears["r2"] + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_community_change_update/__init__.py b/tests/topotests/bgp_community_change_update/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/__init__.py diff --git a/tests/topotests/bgp_community_change_update/c1/bgpd.conf b/tests/topotests/bgp_community_change_update/c1/bgpd.conf new file mode 100644 index 0000000000..24cf9dffb9 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/c1/bgpd.conf @@ -0,0 +1,11 @@ +! +debug bgp updates +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 10.0.1.2 remote-as external + neighbor 10.0.1.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_community_change_update/c1/zebra.conf b/tests/topotests/bgp_community_change_update/c1/zebra.conf new file mode 100644 index 0000000000..e3dbbc051d --- /dev/null +++ b/tests/topotests/bgp_community_change_update/c1/zebra.conf @@ -0,0 +1,6 @@ +! +interface c1-eth0 + ip address 10.0.1.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py b/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py new file mode 100644 index 0000000000..5fc4310266 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python + +# Copyright (c) 2020 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# 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. +# + +""" +Reference: https://www.cmand.org/communityexploration + + --y2-- + / | \ + c1 ---- x1 ---- y1 | z1 + \ | / + --y3-- + +1. z1 announces 192.168.255.254/32 to y2, y3. +2. y2 and y3 tags this prefix at ingress with appropriate +communities 65004:2 (y2) and 65004:3 (y3). +3. x1 filters all communities at the egress to c1. +4. Shutdown the link between y1 and y2. +5. y1 will generate a BGP UPDATE message regarding the next-hop change. +6. x1 will generate a BGP UPDATE message regarding community change. + +To avoid sending duplicate BGP UPDATE messages we should make sure +we send only actual route updates. In this example, x1 will skip +BGP UPDATE to c1 because the actual route is the same +(filtered communities - nothing changes). +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + +from lib.common_config import step +from time import sleep + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + tgen.add_router("z1") + tgen.add_router("y1") + tgen.add_router("y2") + tgen.add_router("y3") + tgen.add_router("x1") + tgen.add_router("c1") + + # 10.0.1.0/30 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["c1"]) + switch.add_link(tgen.gears["x1"]) + + # 10.0.2.0/30 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["x1"]) + switch.add_link(tgen.gears["y1"]) + + # 10.0.3.0/30 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["y1"]) + switch.add_link(tgen.gears["y2"]) + + # 10.0.4.0/30 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["y1"]) + switch.add_link(tgen.gears["y3"]) + + # 10.0.5.0/30 + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["y2"]) + switch.add_link(tgen.gears["y3"]) + + # 10.0.6.0/30 + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["y2"]) + switch.add_link(tgen.gears["z1"]) + + # 10.0.7.0/30 + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["y3"]) + switch.add_link(tgen.gears["z1"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_community_update_path_change(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge_initial(): + output = json.loads( + tgen.gears["c1"].vtysh_cmd("show ip bgp neighbor 10.0.1.2 json") + ) + expected = { + "10.0.1.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 8}}, + } + } + return topotest.json_cmp(output, expected) + + step("Check if an initial topology is converged") + test_func = functools.partial(_bgp_converge_initial) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see bgp convergence in c1" + + step("Disable link between y1 and y2") + tgen.gears["y1"].run("ip link set dev y1-eth1 down") + + def _bgp_converge_link_disabled(): + output = json.loads(tgen.gears["y1"].vtysh_cmd("show ip bgp nei 10.0.3.2 json")) + expected = {"10.0.3.2": {"bgpState": "Active"}} + return topotest.json_cmp(output, expected) + + step("Check if a topology is converged after a link down between y1 and y2") + test_func = functools.partial(_bgp_converge_link_disabled) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see bgp convergence in y1" + + def _bgp_check_for_duplicate_updates(): + duplicate = False + i = 0 + while i < 5: + if ( + len( + tgen.gears["c1"].run( + 'grep "10.0.1.2 rcvd 192.168.255.254/32 IPv4 unicast...duplicate ignored" bgpd.log' + ) + ) + > 0 + ): + duplicate = True + i += 1 + sleep(0.5) + return duplicate + + step("Check if we see duplicate BGP UPDATE message in c1 (suppress-duplicates)") + assert ( + _bgp_check_for_duplicate_updates() == False + ), "Seen duplicate BGP UPDATE message in c1 from x1" + + step("Disable bgp suppress-duplicates at x1") + tgen.gears["x1"].run( + "vtysh -c 'conf' -c 'router bgp' -c 'no bgp suppress-duplicates'" + ) + + step("Enable link between y1 and y2") + tgen.gears["y1"].run("ip link set dev y1-eth1 up") + + def _bgp_converge_link_enabled(): + output = json.loads(tgen.gears["y1"].vtysh_cmd("show ip bgp nei 10.0.3.2 json")) + expected = { + "10.0.3.2": { + "bgpState": "Established", + "addressFamilyInfo": { + "ipv4Unicast": {"acceptedPrefixCounter": 5, "sentPrefixCounter": 4} + }, + } + } + return topotest.json_cmp(output, expected) + + step("Check if a topology is converged after a link up between y1 and y2") + test_func = functools.partial(_bgp_converge_link_enabled) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see bgp convergence in y1" + + step( + "Check if we see duplicate BGP UPDATE message in c1 (no bgp suppress-duplicates)" + ) + assert ( + _bgp_check_for_duplicate_updates() == True + ), "Didn't see duplicate BGP UPDATE message in c1 from x1" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_community_change_update/x1/bgpd.conf b/tests/topotests/bgp_community_change_update/x1/bgpd.conf new file mode 100644 index 0000000000..8d7bcb948b --- /dev/null +++ b/tests/topotests/bgp_community_change_update/x1/bgpd.conf @@ -0,0 +1,20 @@ +! +debug bgp updates +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 10.0.1.1 remote-as external + neighbor 10.0.1.1 timers 3 10 + neighbor 10.0.2.2 remote-as external + neighbor 10.0.2.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + neighbor 10.0.1.1 route-map c1 out + exit-address-family +! +bgp community-list standard c1 seq 1 permit 65004:2 +bgp community-list standard c1 seq 2 permit 65004:3 +! +route-map c1 permit 10 + set comm-list c1 delete +! diff --git a/tests/topotests/bgp_community_change_update/x1/zebra.conf b/tests/topotests/bgp_community_change_update/x1/zebra.conf new file mode 100644 index 0000000000..8b7c03ffbf --- /dev/null +++ b/tests/topotests/bgp_community_change_update/x1/zebra.conf @@ -0,0 +1,9 @@ +! +interface x1-eth0 + ip address 10.0.1.2/30 +! +interface x1-eth1 + ip address 10.0.2.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_community_change_update/y1/bgpd.conf b/tests/topotests/bgp_community_change_update/y1/bgpd.conf new file mode 100644 index 0000000000..a010085835 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 10.0.2.1 remote-as external + neighbor 10.0.2.1 timers 3 10 + neighbor 10.0.3.2 remote-as internal + neighbor 10.0.3.2 timers 3 10 + neighbor 10.0.4.2 remote-as internal + neighbor 10.0.4.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_community_change_update/y1/zebra.conf b/tests/topotests/bgp_community_change_update/y1/zebra.conf new file mode 100644 index 0000000000..4096f2a2e3 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y1/zebra.conf @@ -0,0 +1,12 @@ +! +interface y1-eth0 + ip address 10.0.2.2/30 +! +interface y1-eth1 + ip address 10.0.3.1/30 +! +interface y1-eth2 + ip address 10.0.4.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_community_change_update/y2/bgpd.conf b/tests/topotests/bgp_community_change_update/y2/bgpd.conf new file mode 100644 index 0000000000..27f1e81f7d --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y2/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 10.0.3.1 remote-as internal + neighbor 10.0.3.1 timers 3 10 + neighbor 10.0.5.2 remote-as internal + neighbor 10.0.5.2 timers 3 10 + neighbor 10.0.6.2 remote-as external + neighbor 10.0.6.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + neighbor 10.0.3.1 route-reflector-client + neighbor 10.0.5.2 route-reflector-client + neighbor 10.0.6.2 route-map z1 in + exit-address-family +! +route-map z1 permit 10 + set community 65004:2 +! diff --git a/tests/topotests/bgp_community_change_update/y2/zebra.conf b/tests/topotests/bgp_community_change_update/y2/zebra.conf new file mode 100644 index 0000000000..7e9458a45c --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y2/zebra.conf @@ -0,0 +1,12 @@ +! +interface y2-eth0 + ip address 10.0.3.2/30 +! +interface y2-eth1 + ip address 10.0.5.1/30 +! +interface y2-eth2 + ip address 10.0.6.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_community_change_update/y3/bgpd.conf b/tests/topotests/bgp_community_change_update/y3/bgpd.conf new file mode 100644 index 0000000000..8e57284929 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y3/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 10.0.4.1 remote-as internal + neighbor 10.0.4.1 timers 3 10 + neighbor 10.0.5.1 remote-as internal + neighbor 10.0.5.1 timers 3 10 + neighbor 10.0.7.2 remote-as external + neighbor 10.0.7.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + neighbor 10.0.4.1 route-reflector-client + neighbor 10.0.5.1 route-reflector-client + neighbor 10.0.7.2 route-map z1 in + exit-address-family +! +route-map z1 permit 10 + set community 65004:3 +! diff --git a/tests/topotests/bgp_community_change_update/y3/zebra.conf b/tests/topotests/bgp_community_change_update/y3/zebra.conf new file mode 100644 index 0000000000..93dd87dbd2 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/y3/zebra.conf @@ -0,0 +1,12 @@ +! +interface y3-eth0 + ip address 10.0.4.2/30 +! +interface y3-eth1 + ip address 10.0.5.2/30 +! +interface y3-eth2 + ip address 10.0.7.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_community_change_update/z1/bgpd.conf b/tests/topotests/bgp_community_change_update/z1/bgpd.conf new file mode 100644 index 0000000000..2f470f18b8 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/z1/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65004 + no bgp ebgp-requires-policy + neighbor 10.0.6.1 remote-as external + neighbor 10.0.6.1 timers 3 10 + neighbor 10.0.7.1 remote-as external + neighbor 10.0.7.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_community_change_update/z1/zebra.conf b/tests/topotests/bgp_community_change_update/z1/zebra.conf new file mode 100644 index 0000000000..7f6bbb66b3 --- /dev/null +++ b/tests/topotests/bgp_community_change_update/z1/zebra.conf @@ -0,0 +1,12 @@ +! +interface lo + ip address 192.168.255.254/32 +! +interface z1-eth0 + ip address 10.0.6.2/30 +! +interface z1-eth1 + ip address 10.0.7.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_features/exabgp.env b/tests/topotests/bgp_features/exabgp.env new file mode 100644 index 0000000000..6c554f5fa8 --- /dev/null +++ b/tests/topotests/bgp_features/exabgp.env @@ -0,0 +1,53 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_features/peer1/exa_readpipe.py b/tests/topotests/bgp_features/peer1/exa_readpipe.py new file mode 100644 index 0000000000..dba1536388 --- /dev/null +++ b/tests/topotests/bgp_features/peer1/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, 'r') + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_features/peer1/exabgp.cfg b/tests/topotests/bgp_features/peer1/exabgp.cfg new file mode 100644 index 0000000000..2e95252cf6 --- /dev/null +++ b/tests/topotests/bgp_features/peer1/exabgp.cfg @@ -0,0 +1,12 @@ +group exabgp { + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in"; + encoder text; + } + neighbor 192.168.101.1 { + router-id 192.168.101.3; + local-address 192.168.101.3; + local-as 65403; + peer-as 65000; + } +} diff --git a/tests/topotests/bgp_features/peer2/exa_readpipe.py b/tests/topotests/bgp_features/peer2/exa_readpipe.py new file mode 100644 index 0000000000..dba1536388 --- /dev/null +++ b/tests/topotests/bgp_features/peer2/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, 'r') + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_features/peer2/exabgp.cfg b/tests/topotests/bgp_features/peer2/exabgp.cfg new file mode 100644 index 0000000000..1f65547bc5 --- /dev/null +++ b/tests/topotests/bgp_features/peer2/exabgp.cfg @@ -0,0 +1,12 @@ +group exabgp { + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in"; + encoder text; + } + neighbor 192.168.101.1 { + router-id 192.168.101.4; + local-address 192.168.101.4; + local-as 65404; + peer-as 65000; + } +} diff --git a/tests/topotests/bgp_features/peer3/exa_readpipe.py b/tests/topotests/bgp_features/peer3/exa_readpipe.py new file mode 100644 index 0000000000..dba1536388 --- /dev/null +++ b/tests/topotests/bgp_features/peer3/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, 'r') + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_features/peer3/exabgp.cfg b/tests/topotests/bgp_features/peer3/exabgp.cfg new file mode 100644 index 0000000000..8632cc86c5 --- /dev/null +++ b/tests/topotests/bgp_features/peer3/exabgp.cfg @@ -0,0 +1,12 @@ +group exabgp { + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in"; + encoder text; + } + neighbor 192.168.101.1 { + router-id 192.168.101.5; + local-address 192.168.101.5; + local-as 65405; + peer-as 65000; + } +} diff --git a/tests/topotests/bgp_features/peer4/exa_readpipe.py b/tests/topotests/bgp_features/peer4/exa_readpipe.py new file mode 100644 index 0000000000..dba1536388 --- /dev/null +++ b/tests/topotests/bgp_features/peer4/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, 'r') + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_features/peer4/exabgp.cfg b/tests/topotests/bgp_features/peer4/exabgp.cfg new file mode 100644 index 0000000000..06bc0d6e64 --- /dev/null +++ b/tests/topotests/bgp_features/peer4/exabgp.cfg @@ -0,0 +1,12 @@ +group exabgp { + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in"; + encoder text; + } + neighbor 192.168.101.1 { + router-id 192.168.101.6; + local-address 192.168.101.6; + local-as 65406; + peer-as 65000; + } +} diff --git a/tests/topotests/bgp_features/r1/bgp_damp_announced.json b/tests/topotests/bgp_features/r1/bgp_damp_announced.json new file mode 100644 index 0000000000..cb4a2c9b2f --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_damp_announced.json @@ -0,0 +1,21 @@ +{ + "localAS":65000, + "routes":{ + "192.168.31.0/24": [ { "valid":true, "network":"192.168.31.0\/24", "peerId":"192.168.101.3" } ], + "192.168.32.0/24": [ { "valid":true, "network":"192.168.32.0\/24", "peerId":"192.168.101.3" } ], + "192.168.33.0/24": [ { "valid":true, "network":"192.168.33.0\/24", "peerId":"192.168.101.3" } ], + "192.168.34.0/24": [ { "valid":true, "network":"192.168.34.0\/24", "peerId":"192.168.101.3" } ], + "192.168.41.0/24": [ { "valid":true, "network":"192.168.41.0\/24", "peerId":"192.168.101.4" } ], + "192.168.42.0/24": [ { "valid":true, "network":"192.168.42.0\/24", "peerId":"192.168.101.4" } ], + "192.168.43.0/24": [ { "valid":true, "network":"192.168.43.0\/24", "peerId":"192.168.101.4" } ], + "192.168.44.0/24": [ { "valid":true, "network":"192.168.44.0\/24", "peerId":"192.168.101.4" } ], + "192.168.51.0/24": [ { "valid":true, "network":"192.168.51.0\/24", "peerId":"192.168.101.5" } ], + "192.168.52.0/24": [ { "valid":true, "network":"192.168.52.0\/24", "peerId":"192.168.101.5" } ], + "192.168.53.0/24": [ { "valid":true, "network":"192.168.53.0\/24", "peerId":"192.168.101.5" } ], + "192.168.54.0/24": [ { "valid":true, "network":"192.168.54.0\/24", "peerId":"192.168.101.5" } ], + "192.168.61.0/24": [ { "valid":true, "network":"192.168.61.0\/24", "peerId":"192.168.101.6" } ], + "192.168.62.0/24": [ { "valid":true, "network":"192.168.62.0\/24", "peerId":"192.168.101.6" } ], + "192.168.63.0/24": [ { "valid":true, "network":"192.168.63.0\/24", "peerId":"192.168.101.6" } ], + "192.168.64.0/24": [ { "valid":true, "network":"192.168.64.0\/24", "peerId":"192.168.101.6" } ] + } +} diff --git a/tests/topotests/bgp_features/r1/bgp_damp_setup.json b/tests/topotests/bgp_features/r1/bgp_damp_setup.json new file mode 100644 index 0000000000..f9f89db894 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_damp_setup.json @@ -0,0 +1,10 @@ +{ + "ipv4Unicast":{ + "peers":{ + "192.168.101.3":{"remoteAs":65403, "state":"Established"}, + "192.168.101.4":{"remoteAs":65404, "state":"Established"}, + "192.168.101.5":{"remoteAs":65405, "state":"Established"}, + "192.168.101.6":{"remoteAs":65406, "state":"Established"} + } + } +} diff --git a/tests/topotests/bgp_features/r2/bgp_damp_announced.json b/tests/topotests/bgp_features/r2/bgp_damp_announced.json new file mode 100644 index 0000000000..9394358f82 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_damp_announced.json @@ -0,0 +1,21 @@ +{ + "localAS":65000, + "routes":{ + "192.168.31.0/24": [ { "network":"192.168.31.0\/24", "peerId":"192.168.0.1" } ], + "192.168.32.0/24": [ { "network":"192.168.32.0\/24", "peerId":"192.168.0.1" } ], + "192.168.33.0/24": [ { "network":"192.168.33.0\/24", "peerId":"192.168.0.1" } ], + "192.168.34.0/24": [ { "network":"192.168.34.0\/24", "peerId":"192.168.0.1" } ], + "192.168.41.0/24": [ { "network":"192.168.41.0\/24", "peerId":"192.168.0.1" } ], + "192.168.42.0/24": [ { "network":"192.168.42.0\/24", "peerId":"192.168.0.1" } ], + "192.168.43.0/24": [ { "network":"192.168.43.0\/24", "peerId":"192.168.0.1" } ], + "192.168.44.0/24": [ { "network":"192.168.44.0\/24", "peerId":"192.168.0.1" } ], + "192.168.51.0/24": [ { "network":"192.168.51.0\/24", "peerId":"192.168.0.1" } ], + "192.168.52.0/24": [ { "network":"192.168.52.0\/24", "peerId":"192.168.0.1" } ], + "192.168.53.0/24": [ { "network":"192.168.53.0\/24", "peerId":"192.168.0.1" } ], + "192.168.54.0/24": [ { "network":"192.168.54.0\/24", "peerId":"192.168.0.1" } ], + "192.168.61.0/24": [ { "network":"192.168.61.0\/24", "peerId":"192.168.0.1" } ], + "192.168.62.0/24": [ { "network":"192.168.62.0\/24", "peerId":"192.168.0.1" } ], + "192.168.63.0/24": [ { "network":"192.168.63.0\/24", "peerId":"192.168.0.1" } ], + "192.168.64.0/24": [ { "network":"192.168.64.0\/24", "peerId":"192.168.0.1" } ] + } +} diff --git a/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json b/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json new file mode 100644 index 0000000000..f3c54a70a1 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json @@ -0,0 +1,18 @@ +{ + "192.168.31.0/24": null, + "192.168.32.0/24": null, + "192.168.33.0/24": null, + "192.168.34.0/24": null, + "192.168.41.0/24": null, + "192.168.42.0/24": null, + "192.168.43.0/24": null, + "192.168.44.0/24": null, + "192.168.51.0/24": null, + "192.168.52.0/24": null, + "192.168.53.0/24": null, + "192.168.54.0/24": null, + "192.168.61.0/24": null, + "192.168.62.0/24": null, + "192.168.63.0/24": null, + "192.168.64.0/24": null +} diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py index bc821bd7a0..3d963b4cf6 100644 --- a/tests/topotests/bgp_features/test_bgp_features.py +++ b/tests/topotests/bgp_features/test_bgp_features.py @@ -33,6 +33,7 @@ import sys import pytest import re import time +from time import sleep # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) @@ -64,6 +65,14 @@ class BGPFeaturesTopo1(Topo): for rtrNum in range(1, 6): tgen.add_router("r{}".format(rtrNum)) + # create ExaBGP peers + for peer_num in range(1, 5): + tgen.add_exabgp_peer( + "peer{}".format(peer_num), + ip="192.168.101.{}".format(peer_num + 2), + defaultRoute="via 192.168.101.1", + ) + # Setup Switches and connections for swNum in range(1, 11): tgen.add_switch("sw{}".format(swNum)) @@ -89,6 +98,12 @@ class BGPFeaturesTopo1(Topo): tgen.gears["r2"].add_link(tgen.gears["sw5"]) tgen.gears["r5"].add_link(tgen.gears["sw5"]) + # Add ExaBGP peers to sw4 + tgen.gears["peer1"].add_link(tgen.gears["sw4"]) + tgen.gears["peer2"].add_link(tgen.gears["sw4"]) + tgen.gears["peer3"].add_link(tgen.gears["sw4"]) + tgen.gears["peer4"].add_link(tgen.gears["sw4"]) + ##################################################### # @@ -753,41 +768,71 @@ def test_bgp_delayopen_without(): pytest.skip(tgen.errors) # part 1: no delay r1 <=> no delay r4 - logger.info("Starting optional test of BGP functionality without DelayOpenTimer enabled to establish a reference for following tests") + logger.info( + "Starting optional test of BGP functionality without DelayOpenTimer enabled to establish a reference for following tests" + ) # 1.1 enable peering shutdown logger.info("Enable shutdown of peering between r1 and r4") - tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 shutdown"') - tgen.net["r4"].cmd('vtysh -c "conf t" -c "router bgp 65100" -c "neighbor 192.168.101.1 shutdown"') + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 shutdown"' + ) + tgen.net["r4"].cmd( + 'vtysh -c "conf t" -c "router bgp 65100" -c "neighbor 192.168.101.1 shutdown"' + ) # 1.2 wait for peers to shut down (poll output) for router_num in [1, 4]: - logger.info("Checking BGP summary after enabling shutdown of peering on r{}".format(router_num)) + logger.info( + "Checking BGP summary after enabling shutdown of peering on r{}".format( + router_num + ) + ) router = tgen.gears["r{}".format(router_num)] - reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num)) + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num) + ) expected = json.loads(open(reffile).read()) - test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) assertmsg = "BGP session on r{} did not shut down peer".format(router_num) assert res is None, assertmsg # 1.3 disable peering shutdown logger.info("Disable shutdown of peering between r1 and r4") - tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"') - tgen.net["r4"].cmd('vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"') + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"' + ) + tgen.net["r4"].cmd( + 'vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"' + ) # 1.4 wait for peers to establish connection (poll output) for router_num in [1, 4]: - logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) router = tgen.gears["r{}".format(router_num)] - reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num)) + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num) + ) expected = json.loads(open(reffile).read()) - test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) - assertmsg = "BGP session on r{} did not establish a connection with peer".format(router_num) + assertmsg = ( + "BGP session on r{} did not establish a connection with peer".format( + router_num + ) + ) assert res is None, assertmsg - #tgen.mininet_cli() + # tgen.mininet_cli() # end test_bgp_delayopen_without @@ -800,65 +845,107 @@ def test_bgp_delayopen_singular(): pytest.skip(tgen.errors) # part 2: delay 240s r1 <=> no delay r4 - logger.info("Starting test of BGP functionality and behaviour with DelayOpenTimer enabled on one side of the peering") + logger.info( + "Starting test of BGP functionality and behaviour with DelayOpenTimer enabled on one side of the peering" + ) # 2.1 enable peering shutdown logger.info("Enable shutdown of peering between r1 and r4") - tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 shutdown"') - tgen.net["r4"].cmd('vtysh -c "conf t" -c "router bgp 65100" -c "neighbor 192.168.101.1 shutdown"') + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 shutdown"' + ) + tgen.net["r4"].cmd( + 'vtysh -c "conf t" -c "router bgp 65100" -c "neighbor 192.168.101.1 shutdown"' + ) # 2.2 wait for peers to shut down (poll output) for router_num in [1, 4]: - logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) router = tgen.gears["r{}".format(router_num)] - reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num)) + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num) + ) expected = json.loads(open(reffile).read()) - test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) assertmsg = "BGP session on r{} did not shut down peer".format(router_num) assert res is None, assertmsg # 2.3 set delayopen on R1 to 240 logger.info("Setting DelayOpenTime for neighbor r4 to 240 seconds on r1") - tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 timers delayopen 240"') + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 timers delayopen 240"' + ) # 2.4 check config (poll output) logger.info("Checking BGP neighbor configuration after setting DelayOpenTime on r1") router = tgen.gears["r1"] reffile = os.path.join(CWD, "r1/bgp_delayopen_neighbor.json") expected = json.loads(open(reffile).read()) - test_func = functools.partial(topotest.router_json_cmp, router, "show bgp neighbors json", expected) + test_func = functools.partial( + topotest.router_json_cmp, router, "show bgp neighbors json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) assertmsg = "BGP session on r1 failed to set DelayOpenTime for r4" assert res is None, assertmsg # 2.5 disable peering shutdown logger.info("Disable shutdown of peering between r1 and r4") - tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"') - tgen.net["r4"].cmd('vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"') + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"' + ) + tgen.net["r4"].cmd( + 'vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"' + ) # 2.6 wait for peers to establish connection (poll output) for router_num in [1, 4]: - logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) router = tgen.gears["r{}".format(router_num)] - reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num)) + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num) + ) expected = json.loads(open(reffile).read()) - test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) - assertmsg = "BGP session on r{} did not establish a connection with peer".format(router_num) + assertmsg = ( + "BGP session on r{} did not establish a connection with peer".format( + router_num + ) + ) assert res is None, assertmsg # 2.7 unset delayopen on R1 logger.info("Disabling DelayOpenTimer for neighbor r4 on r1") - tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 timers delayopen"') + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 timers delayopen"' + ) # 2.8 check config (poll output) - logger.info("Checking BGP neighbor configuration after disabling DelayOpenTimer on r1") - delayopen_cfg = tgen.net["r1"].cmd('vtysh -c "show bgp neighbors json" | grep "DelayOpenTimeMsecs"').rstrip() + logger.info( + "Checking BGP neighbor configuration after disabling DelayOpenTimer on r1" + ) + delayopen_cfg = ( + tgen.net["r1"] + .cmd('vtysh -c "show bgp neighbors json" | grep "DelayOpenTimeMsecs"') + .rstrip() + ) assertmsg = "BGP session on r1 failed disable DelayOpenTimer for peer r4" assert delayopen_cfg == "", assertmsg - #tgen.mininet_cli() + # tgen.mininet_cli() # end test_bgp_delayopen_singular @@ -870,67 +957,119 @@ def test_bgp_delayopen_dual(): pytest.skip(tgen.errors) # part 3: delay 60s R2 <=> delay 30s R5 - logger.info("Starting test of BGP functionality and behaviour with DelayOpenTimer enabled on both sides of the peering with different timer intervals") + logger.info( + "Starting test of BGP functionality and behaviour with DelayOpenTimer enabled on both sides of the peering with different timer intervals" + ) # 3.1 enable peering shutdown logger.info("Enable shutdown of peering between r2 and r5") - tgen.net["r2"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.201.2 shutdown"') - tgen.net["r5"].cmd('vtysh -c "conf t" -c "router bgp 65200" -c "neighbor 192.168.201.1 shutdown"') + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.201.2 shutdown"' + ) + tgen.net["r5"].cmd( + 'vtysh -c "conf t" -c "router bgp 65200" -c "neighbor 192.168.201.1 shutdown"' + ) # 3.2 wait for peers to shut down (pool output) for router_num in [2, 5]: - logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) router = tgen.gears["r{}".format(router_num)] - reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num)) + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num) + ) expected = json.loads(open(reffile).read()) - test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) assertmsg = "BGP session on r{} did not shut down peer".format(router_num) assert res is None, assertmsg # 3.3 set delayopen on R2 to 60s and on R5 to 30s logger.info("Setting DelayOpenTime for neighbor r5 to 60 seconds on r2") - tgen.net["r2"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.201.2 timers delayopen 60"') + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.201.2 timers delayopen 60"' + ) logger.info("Setting DelayOpenTime for neighbor r2 to 30 seconds on r5") - tgen.net["r5"].cmd('vtysh -c "conf t" -c "router bgp 65200" -c "neighbor 192.168.201.1 timers delayopen 30"') + tgen.net["r5"].cmd( + 'vtysh -c "conf t" -c "router bgp 65200" -c "neighbor 192.168.201.1 timers delayopen 30"' + ) # 3.4 check config (poll output) for router_num in [2, 5]: - logger.info("Checking BGP neighbor configuration after setting DelayOpenTime on r{}i".format(router_num)) + logger.info( + "Checking BGP neighbor configuration after setting DelayOpenTime on r{}i".format( + router_num + ) + ) router = tgen.gears["r{}".format(router_num)] - reffile = os.path.join(CWD, "r{}/bgp_delayopen_neighbor.json".format(router_num)) + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_neighbor.json".format(router_num) + ) expected = json.loads(open(reffile).read()) - test_func = functools.partial(topotest.router_json_cmp, router, "show bgp neighbors json", expected) + test_func = functools.partial( + topotest.router_json_cmp, router, "show bgp neighbors json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) assertmsg = "BGP session on r{} failed to set DelayOpenTime".format(router_num) assert res is None, assertmsg ## 3.5 disable peering shutdown logger.info("Disable shutdown of peering between r2 and r5") - tgen.net["r2"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.201.2 shutdown"') - tgen.net["r5"].cmd('vtysh -c "conf t" -c "router bgp 65200" -c "no neighbor 192.168.201.1 shutdown"') + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.201.2 shutdown"' + ) + tgen.net["r5"].cmd( + 'vtysh -c "conf t" -c "router bgp 65200" -c "no neighbor 192.168.201.1 shutdown"' + ) ## 3.6 wait for peers to reach connect or active state (poll output) delay_start = int(time.time()) for router_num in [2, 5]: - logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) router = tgen.gears["r{}".format(router_num)] - reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_connect.json".format(router_num)) + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_connect.json".format(router_num) + ) expected = json.loads(open(reffile).read()) - test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) - assertmsg = "BGP session on r{} did not enter Connect state with peer".format(router_num) + assertmsg = "BGP session on r{} did not enter Connect state with peer".format( + router_num + ) assert res is None, assertmsg ## 3.7 wait for peers to establish connection (poll output) for router_num in [2, 5]: - logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + logger.info( + "Checking BGP summary after disabling shutdown of peering on r{}".format( + router_num + ) + ) router = tgen.gears["r{}".format(router_num)] - reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num)) + reffile = os.path.join( + CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num) + ) expected = json.loads(open(reffile).read()) - test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=35, wait=1) - assertmsg = "BGP session on r{} did not establish a connection with peer".format(router_num) + assertmsg = ( + "BGP session on r{} did not establish a connection with peer".format( + router_num + ) + ) assert res is None, assertmsg delay_stop = int(time.time()) @@ -939,22 +1078,692 @@ def test_bgp_delayopen_dual(): # 3.8 unset delayopen on R2 and R5 logger.info("Disabling DelayOpenTimer for neighbor r5 on r2") - tgen.net["r2"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.201.2 timers delayopen"') + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.201.2 timers delayopen"' + ) logger.info("Disabling DelayOpenTimer for neighbor r2 on r5") - tgen.net["r5"].cmd('vtysh -c "conf t" -c "router bgp 65200" -c "no neighbor 192.168.201.1 timers delayopen"') + tgen.net["r5"].cmd( + 'vtysh -c "conf t" -c "router bgp 65200" -c "no neighbor 192.168.201.1 timers delayopen"' + ) # 3.9 check config (poll output) for router_num in [2, 5]: - logger.info("Checking BGP neighbor configuration after disabling DelayOpenTimer on r{}".format(router_num)) - delayopen_cfg = tgen.net["r{}".format(router_num)].cmd('vtysh -c "show bgp neighbors json" | grep "DelayOpenTimeMsecs"').rstrip() - assertmsg = "BGP session on r{} failed disable DelayOpenTimer".format(router_num) + logger.info( + "Checking BGP neighbor configuration after disabling DelayOpenTimer on r{}".format( + router_num + ) + ) + delayopen_cfg = ( + tgen.net["r{}".format(router_num)] + .cmd('vtysh -c "show bgp neighbors json" | grep "DelayOpenTimeMsecs"') + .rstrip() + ) + assertmsg = "BGP session on r{} failed disable DelayOpenTimer".format( + router_num + ) assert delayopen_cfg == "", assertmsg - #tgen.mininet_cli() + # tgen.mininet_cli() # end test_bgp_delayopen_dual +def test_bgp_dampening_setup(): + "BGP route-flap dampening test setup" + + # This test starts four ExaBGP peers, adds them as neighbors to the + # configuration of router r1 and checks if connections get established. + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting BGP route-flap dampening test setup") + + # Start ExaBGP peers connected to r1 via switch 4 + logger.info("Starting ExaBGP peers") + for peer_num in range(1, 5): + logger.info("Creating named pipe for ExaBGP peer peer{}".format(peer_num)) + fifo_in = "/var/run/exabgp_peer{}.in".format(peer_num) + if os.path.exists(fifo_in): + os.remove(fifo_in) + os.mkfifo(fifo_in, 0o777) + logger.info("Starting ExaBGP on peer peer{}".format(peer_num)) + peer = tgen.gears["peer{}".format(peer_num)] + peer_dir = os.path.join(CWD, "peer{}".format(peer_num)) + env_file = os.path.join(CWD, "exabgp.env") + peer.start(peer_dir, env_file) + + # Add ExaBGP peers to configuration of router r2 + logger.info("Adding ExaBGP peers as neighbors to configuration of router r2") + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.3 remote-as 65403"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 route-map testmap-in"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 route-map testmap-out"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.4 remote-as 65404"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.4 route-map testmap-in"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.4 route-map testmap-out"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.5 remote-as 65405"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 route-map testmap-in"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 route-map testmap-out"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.6 remote-as 65406"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.6 route-map testmap-in"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.6 route-map testmap-out"' + ) + + # Check if exabgp peers are up and running + logger.info("Checking for established connections to ExaBGP peers on router r1") + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/bgp_damp_setup.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertmsg = ( + "BGP session on r1 did not establish connections with one ore more ExaBGP peers" + ) + assert res is None, assertmsg + + # end test_bgp_dampening_setup + + +def test_bgp_dampening_route_announce(): + "Test of BGP route-flap dampening route announcement" + + # This test checks if the four ExaBGP peers can announce routes to router + # r1 and if these routes get forwarded to router r2. + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting test of BGP route-flap dampening route announcement") + + # Announce routes on exabgp peers to r2 + logger.info("Announcing routes on ExaBGP peers to r1") + for prefix_iter in range(1, 5): + for peer_num in range(1, 5): + pipe = open("/run/exabgp_peer{}.in".format(peer_num), "w") + with pipe: + pipe.write( + "announce route 192.168.{}{}.0/24 next-hop 192.168.101.{}\n".format( + (peer_num + 2), prefix_iter, (peer_num + 2) + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Check if routes announced by ExaBGP peers are present in RIB of router r1 + logger.info( + "Checking if routes announced by ExaBGP peers are present in RIB of router r1" + ) + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/bgp_damp_announced.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertmsg = ( + "BGP session on router r1 did not receive routes announced by ExaBGP peers" + ) + assert res is None, assertmsg + + # Check if routes announced by ExaBGP peers to router r1 have been forwarded + # and are now present in RIB of router r2 + logger.info( + "Checking if forwarded routes announced by ExaBGP peers are present in RIB of router r2" + ) + router = tgen.gears["r2"] + reffile = os.path.join(CWD, "r2/bgp_damp_announced.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertmsg = "BGP session on router r2 did not receive routes announced by ExaBGP peers forwarded by router r1" + assert res is None, assertmsg + + # end test_bgp_dampening_route_announce + + +def test_bgp_dampening_disabled(): + "Test of BGP route-flapping with dampening disabled" + + # This test verifies that flapped routes do not get withdrawn from the RIB + # of router r1 if dampening is disabled. + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting test of BGP route-flapping with dampening disabled") + + # Flapping routes on ExaBGP peer peer1 + logger.info( + "Flapping routes on ExaBGP peer peer1 with route-flap dampening disabled" + ) + for _ in range(1, 5): + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer1.in", "w") + with pipe: + pipe.write( + "withdraw route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + sleep(1) # Give the BGP session on router r1 time to process routes + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer1.in", "w") + with pipe: + pipe.write( + "announce route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Verify flapped routes are still present in RIB of router r1 + logger.info( + "Verifying that the flapped routes are still present in RIB of router r1" + ) + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/bgp_damp_announced.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertmsg = "BGP session on router r1 removed flapped routes despite route-flap dampening being disabled" + assert res is None, assertmsg + + # end test_bgp_dampening_disabled + + +def test_bgp_dampening_config(): + "Test of BGP route-flap dampening configuration" + + # This test adds peer-group group1 with peers peer1 and peer2 to the + # configuration of router r1, sets up dampening configurations with + # different profiles and verifies the configured dampening parameters. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting test of BGP route-flap dampening configuration") + + # Add peer-group group1 with peers peer1 and peer2 + logger.info( + "Creating peer-group group1 and adding ExaBGP peers peer1 and peer2 to it" + ) + r_1.cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor group1 peer-group"') + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.3 peer-group group1"' + ) # peer1 + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.4 peer-group group1"' + ) # peer2 + + # Enable different dampening profiles for peer1, peer3, group1 and global + # configuration + logger.info( + "Enabling different dampening profiles for peer1, peer3, group1 and global configuration" + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "bgp dampening 30 300 900 90"' + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor group1 dampening 20 200 600 60"' + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 dampening 10 100 300 30"' + ) # peer1 + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 dampening 10 100 300 30"' + ) # peer3 + + # Verify route-flap dampening configuration + logger.info("Verifying route-flap dampening configuration on router r1") + vtyout = r_1.cmd('vtysh -c "show running-config"') + assertmsg = "BGP Session on r1 does not show enabled global route-flap dampening in running configuration" + assert re.search("bgp dampening 30 300 900 90", vtyout), assertmsg + assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer-group group1 in running configuration" + assert re.search("neighbor group1 dampening 20 200 600 60", vtyout), assertmsg + assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer peer1 in running configuration" + assert re.search( + "neighbor 192.168.101.3 dampening 10 100 300 30", vtyout + ), assertmsg + assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer peer3 in running configuration" + assert re.search( + "neighbor 192.168.101.5 dampening 10 100 300 30", vtyout + ), assertmsg + + # end test_bgp_dampening_config + + +def test_bgp_dampening_profile_peer_over_group(): + "Test of BGP route-flap dampening profile preferences: peer over group" + + # This test verifies that the dampening profile of a peer takes precedence + # over the dampening profile of its peer-group by flapping the peers routes + # until dampened and comparing the reuse times to the one specified in the + # dampening configuration. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Starting test of BGP route-flap dampening profile preferences: peer over group" + ) + + # Flapping routes on ExaBGP peer peer1 + logger.info( + "Flapping routes on ExaBGP peer peer1 with route-flap dampening enabled" + ) + for _ in range(1, 5): + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer1.in", "w") + with pipe: + pipe.write( + "withdraw route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + sleep(1) # Give the BGP session on router r1 time to process routes + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer1.in", "w") + with pipe: + pipe.write( + "announce route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Check damped paths on r1 for routes of peer1 witn peer profile + logger.info( + "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer1" + ) + sleep(5) # Wait 5 seconds for paths to show up in dampened-paths list + vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"') + routes = re.findall(r"\*d 192\.168\.3\d\.0\/24.*", vtyout) + assertmsg = ( + "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer1" + ) + assert len(routes) == 4, assertmsg + assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer1" + for route in routes: + assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time + 35 > int(route.split()[3].split(":")[1]) > 25 + ), assertmsg # minutes of reuse time + + # end test_bgp_dampening_profile_peer_over_group + + +def test_bgp_dampening_profile_group_over_global(): + "Test of BGP route-flap dampening profile preferences: group over global" + + # This test verifies that the dampening profile of a peer-group takes + # precedence over the global dampening profile by flapping the routes of a + # peer-group member until dampened and comparing the reuse times to the one + # specified in the dampening configuration. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Starting test of BGP route-flap dampening profile preferences: group over global" + ) + + # Flapping routes on ExaBGP peer peer2 + logger.info( + "Flapping routes on ExaBGP peer peer2 with route-flap dampening enabled" + ) + for _ in range(1, 5): + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer2.in", "w") + with pipe: + pipe.write( + "withdraw route 192.168.4{}.0/24 next-hop 192.168.101.4\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + sleep(1) # Give the BGP session on router r1 time to process routes + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer2.in", "w") + with pipe: + pipe.write( + "announce route 192.168.4{}.0/24 next-hop 192.168.101.4\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Check damped paths on r1 for routes of peer2 witn group profile + logger.info( + "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer2" + ) + sleep(5) # wait 5 seconds for paths to shop up in damp list + vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"') + routes = re.findall(r"\*d 192\.168\.4\d\.0\/24.*", vtyout) + assertmsg = ( + "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer2" + ) + assert len(routes) == 4, assertmsg + assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer2" + for route in routes: + assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time + 65 > int(route.split()[3].split(":")[1]) > 55 + ), assertmsg # minutes of reuse time + + # end test_bgp_dampening_profile_group_over_global + + +def test_bgp_dampening_profile_peer_over_global(): + "Test of BGP route-flap dampening profile preferences: peer over global" + + # This test verifies that the dampening profile of a peer takes precedence + # over the global dampening profile by flapping the routes of the peer until + # dampened and comparing the reuse times to the one specified in the + # dampening configuration. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Starting test of BGP route-flap dampening profile preferences: peer over global" + ) + + # Flapping routes on ExaBGP peer peer3 + logger.info( + "Flapping routes on ExaBGP peer peer3 with route-flap dampening enabled" + ) + for _ in range(1, 5): + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer3.in", "w") + with pipe: + pipe.write( + "withdraw route 192.168.5{}.0/24 next-hop 192.168.101.5\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + sleep(1) # Give the BGP session on router r1 time to process routes + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer3.in", "w") + with pipe: + pipe.write( + "announce route 192.168.5{}.0/24 next-hop 192.168.101.5\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Check damped paths on r1 for routes of peer3 witn peer profile + logger.info( + "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer3" + ) + sleep(5) # wait 5 seconds for paths to shop up in damp list + vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"') + routes = re.findall(r"\*d 192\.168\.5\d\.0\/24.*", vtyout) + assertmsg = ( + "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer3" + ) + assert len(routes) == 4, assertmsg + assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer3" + for route in routes: + assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time + 35 > int(route.split()[3].split(":")[1]) > 25 + ), assertmsg # minutes of reuse time + + # end test_bgp_dampening_profile_peer_over_global + + +def test_bgp_dampening_profile_global(): + "Test of BGP route-flap dampening global profile" + + # This test verifies the application of the global dampening profile by + # flapping the routes of a peer until dampened and comparing the reuse times + # to the one specified in the dampening configuration. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting test of BGP route-flap dampening global profile") + + # Flapping routes on ExaBGP peer peer4 + logger.info( + "Flapping routes on ExaBGP peer peer4 with route-flap dampening enabled" + ) + for _ in range(1, 5): + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer4.in", "w") + with pipe: + pipe.write( + "withdraw route 192.168.6{}.0/24 next-hop 192.168.101.6\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + sleep(1) # Give the BGP session on router r1 time to process routes + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer4.in", "w") + with pipe: + pipe.write( + "announce route 192.168.6{}.0/24 next-hop 192.168.101.6\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Check damped paths on r1 for routes of peer4 witn global profile + logger.info( + "Checking if router r1 used the global dampening profile on routes flapped by ExaBGP peer peer4" + ) + sleep(5) # wait 5 seconds for paths to shop up in damp list + vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"') + routes = re.findall(r"\*d 192\.168\.6\d\.0\/24.*", vtyout) + assertmsg = ( + "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer4" + ) + assert len(routes) == 4, assertmsg + assertmsg = "BGP session on router r1 did not use the global dampening profile for a route flapped by ExaBGP peer peer4" + for route in routes: + assert (int(route.split()[3].split(":")[0]) == 1) and ( # hours of reuse time + 35 > int(route.split()[3].split(":")[1]) > 25 + ), assertmsg # minutes of reuse time + + # end test_bgp_dampening_profile_global + + +def test_bgp_dampening_withdaw(): + "Test BGP route-flap dampening route withdraw" + + # This test verifies that the withrawl of dampened routes from the RIB of + # router r1 was propagated to router r2. + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting test of BGP route-flap dampening route withdraw") + + # Check if routes dampened on router r1 have been withdrawn from the RIB on + # router r2 + logger.info( + "Checking if routes dampened on router r1 have been withdrawn of RIB on router r2" + ) + reffile = os.path.join(CWD, "r2/bgp_damp_withdrawn.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, tgen.gears["r2"], "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "BGP session on router r2 did not receive withdraw of routes dampened on router r1" + assert res is None, assertmsg + + # end test_bgp_dampening_withdaw + + +def test_bgp_dampening_cleanup(): + "BGP route-flap dampening test cleanup" + + # This test cleans up after other tests associated with route-flap dampening + # by disabling all dampening configurations, removing added peers and + # peer-groups from the configuration on router r1, and shutting down ExaBGP + # peers peer1, peer2 and peer3. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting BGP route-flap dampening test cleanup") + + # Disable all dampening configurations + logger.info("Disabling all dampening configurations") + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no bgp dampening"' + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor group1 dampening"' + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor 192.168.101.3 dampening"' + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor 192.168.101.5 dampening"' + ) + + # Remove ExaBGP peers from configuration of router r1 + logger.info("Removing ExaBGP peers from configuration of router r1") + for router_num in range(3, 7): + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.{}"'.format( + router_num + ) + ) + + # Remove peer-group group1 from configuration of router r1 + logger.info("Removing peer-group group1 peers from configuration of router r1") + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor group1 peer-group"' + ) + + # Stop ExaBGP peers and remove associated named pipes + logger.info("Stopping ExaBGP peers and removing associated named pipes") + for peer_num in range(1, 5): + logger.info("Terminating ExaBGP on peer peer{}".format(peer_num)) + peer = tgen.gears["peer{}".format(peer_num)] + logger.info("Removing named pipe of ExaBGP peer peer{}".format(peer_num)) + fifo_in = "/var/run/exabgp_peer{}.in".format(peer_num) + peer.stop() + if os.path.exists(fifo_in): + os.remove(fifo_in) + + # end test_bgp_dampening_cleanup + + +def test_bgp_dampening_aftermath(): + "BGP route-flap dampening aftermath test" + + # This test verifies routers r1 and r2 not being affected by the route-flap + # dampening test series. + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check BGP Summary on routers r1 and r2 + for rtr_num in [1, 2]: + logger.info( + "Checking if BGP router on r{} remains unaffected by route-flap dampening tests".format( + rtr_num + ) + ) + router = tgen.gears["r{}".format(rtr_num)] + reffile = os.path.join(CWD, "r{}/show_bgp.json".format(rtr_num)) + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=2) + assertmsg = "BGP routes on router r{} are wrong after route-flap dampening tests".format( + rtr_num + ) + assert res is None, assertmsg + + # end test_bgp_dampening_aftermath + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py index 097b654e77..3a2d283025 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py @@ -316,7 +316,9 @@ def test_BGP_GR_TC_46_p1(request): input_dict = { "r1": { "bgp": { - "graceful-restart": {"graceful-restart": True,}, + "graceful-restart": { + "graceful-restart": True, + }, "address_family": { "ipv4": { "unicast": { @@ -1184,8 +1186,8 @@ def test_BGP_GR_TC_53_p1(request): def test_BGP_GR_TC_4_p0(request): """ - Test Objective : Verify that the restarting node sets "R" bit while sending the - BGP open messages after the node restart, only if GR is enabled. + Test Objective : Verify that the restarting node sets "R" bit while sending the + BGP open messages after the node restart, only if GR is enabled. """ tgen = get_topogen() @@ -1368,9 +1370,9 @@ def test_BGP_GR_TC_4_p0(request): def test_BGP_GR_TC_5_1_2_p1(request): """ - Test Objective : Verify if restarting node resets R bit in BGP open message - during normal BGP session flaps as well, even when GR restarting mode is enabled. - Here link flap happen due to interface UP/DOWN. + Test Objective : Verify if restarting node resets R bit in BGP open message + during normal BGP session flaps as well, even when GR restarting mode is enabled. + Here link flap happen due to interface UP/DOWN. """ tgen = get_topogen() @@ -1486,7 +1488,7 @@ def test_BGP_GR_TC_5_1_2_p1(request): shutdown_bringup_interface(tgen, "r2", intf) # Bring up Interface - shutdown_bringup_interface(tgen, dut, intf, ifaceaction=True) + shutdown_bringup_interface(tgen, "r2", intf, ifaceaction=True) for addr_type in ADDR_TYPES: result = verify_graceful_restart( @@ -1773,7 +1775,7 @@ def test_BGP_GR_TC_6_1_2_p1(request): shutdown_bringup_interface(tgen, "r2", intf) # Bring up Interface - shutdown_bringup_interface(tgen, dut, intf, ifaceaction=True) + shutdown_bringup_interface(tgen, "r2", intf, ifaceaction=True) for addr_type in ADDR_TYPES: result = verify_graceful_restart( @@ -1815,8 +1817,8 @@ def test_BGP_GR_TC_6_1_2_p1(request): def test_BGP_GR_TC_8_p1(request): """ - Test Objective : Verify that restarting nodes set "F" bit while sending - the BGP open messages after it restarts, only when BGP GR is enabled. + Test Objective : Verify that restarting nodes set "F" bit while sending + the BGP open messages after it restarts, only when BGP GR is enabled. """ tgen = get_topogen() @@ -1959,8 +1961,8 @@ def test_BGP_GR_TC_8_p1(request): def test_BGP_GR_TC_17_p1(request): """ - Test Objective : Verify that only GR helper routers keep the stale - route entries, not any GR disabled router. + Test Objective : Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. """ tgen = get_topogen() @@ -2145,8 +2147,8 @@ def test_BGP_GR_TC_17_p1(request): def test_BGP_GR_TC_19_p1(request): """ - Test Objective : Verify that GR helper routers keeps all the routes received - from restarting node if both the routers are configured as GR restarting node. + Test Objective : Verify that GR helper routers keeps all the routes received + from restarting node if both the routers are configured as GR restarting node. """ tgen = get_topogen() @@ -2325,8 +2327,8 @@ def test_BGP_GR_TC_19_p1(request): def test_BGP_GR_TC_20_p1(request): """ - Test Objective : Verify that GR helper routers delete all the routes - received from a node if both the routers are configured as GR helper node. + Test Objective : Verify that GR helper routers delete all the routes + received from a node if both the routers are configured as GR helper node. """ tgen = get_topogen() tc_name = request.node.name @@ -3090,8 +3092,8 @@ def test_BGP_GR_TC_31_2_p1(request): def test_BGP_GR_TC_9_p1(request): """ - Test Objective : Verify that restarting nodes reset "F" bit while sending - the BGP open messages after it's restarts, when BGP GR is **NOT** enabled. + Test Objective : Verify that restarting nodes reset "F" bit while sending + the BGP open messages after it's restarts, when BGP GR is **NOT** enabled. """ tgen = get_topogen() @@ -3264,8 +3266,8 @@ def test_BGP_GR_TC_9_p1(request): def test_BGP_GR_TC_17_p1(request): """ - Test Objective : Verify that only GR helper routers keep the stale - route entries, not any GR disabled router. + Test Objective : Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. """ tgen = get_topogen() @@ -3467,7 +3469,13 @@ def test_BGP_GR_TC_43_p1(request): step("Configure R1 and R2 as GR restarting node in global level") input_dict = { - "r1": {"bgp": {"graceful-restart": {"graceful-restart": True,}}}, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, } @@ -3560,7 +3568,13 @@ def test_BGP_GR_TC_43_p1(request): step("Verify on R2 that R1 doesn't advertise any GR capabilities") input_dict = { - "r1": {"bgp": {"graceful-restart": {"graceful-restart-disable": True,}}}, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-disable": True, + } + } + }, "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, } @@ -3659,7 +3673,13 @@ def test_BGP_GR_TC_43_p1(request): step("Verify on R2 that R1 advertises GR capabilities as a restarting node") input_dict = { - "r1": {"bgp": {"graceful-restart": {"graceful-restart": True,}}}, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, } @@ -3779,7 +3799,13 @@ def test_BGP_GR_TC_44_p1(request): step("Verify on R2 that R1 advertises GR capabilities as a helper node") input_dict = { - "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True,}}}, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-helper": True, + } + } + }, "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, } @@ -3849,7 +3875,13 @@ def test_BGP_GR_TC_44_p1(request): start_router_daemons(tgen, "r2", ["bgpd"]) input_dict = { - "r1": {"bgp": {"graceful-restart": {"graceful-restart-disable": True,}}} + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-disable": True, + } + } + } } configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") @@ -3857,7 +3889,13 @@ def test_BGP_GR_TC_44_p1(request): step("Verify on R2 that R1 doesn't advertise any GR capabilities") input_dict = { - "r1": {"bgp": {"graceful-restart": {"graceful-restart-disable": True,}}}, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-disable": True, + } + } + }, "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, } @@ -3941,7 +3979,13 @@ def test_BGP_GR_TC_44_p1(request): step("Verify on R2 that R1 advertises GR capabilities as a helper node") input_dict = { - "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True,}}}, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-helper": True, + } + } + }, "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, } @@ -4108,14 +4152,28 @@ def test_BGP_GR_TC_45_p1(request): start_router_daemons(tgen, "r1", ["bgpd"]) - input_dict = {"r1": {"bgp": {"graceful-restart": {"graceful-restart": False,}}}} + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": False, + } + } + } + } configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") step("Verify on R2 that R1 advertises GR capabilities as a helper node") input_dict = { - "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True,}}}, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart-helper": True, + } + } + }, "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, } @@ -4199,14 +4257,28 @@ def test_BGP_GR_TC_45_p1(request): start_router_daemons(tgen, "r2", ["bgpd"]) - input_dict = {"r1": {"bgp": {"graceful-restart": {"graceful-restart": True,}}}} + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + } + } configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") step("Verify on R2 that R1 advertises GR capabilities as a restarting node") input_dict = { - "r1": {"bgp": {"graceful-restart": {"graceful-restart": True,}}}, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, } @@ -4307,7 +4379,9 @@ def test_BGP_GR_TC_46_p1(request): input_dict = { "r1": { "bgp": { - "graceful-restart": {"graceful-restart": True,}, + "graceful-restart": { + "graceful-restart": True, + }, "address_family": { "ipv4": { "unicast": { @@ -4559,7 +4633,9 @@ def test_BGP_GR_TC_47_p1(request): input_dict = { "r1": { "bgp": { - "graceful-restart": {"graceful-restart": True,}, + "graceful-restart": { + "graceful-restart": True, + }, "address_family": { "ipv4": { "unicast": { @@ -4698,7 +4774,13 @@ def test_BGP_GR_TC_47_p1(request): step("Verify on R2 that R1 still advertises GR capabilities as a restarting node") input_dict = { - "r1": {"bgp": {"graceful-restart": {"graceful-restart": True,}}}, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, } @@ -4814,7 +4896,9 @@ def test_BGP_GR_TC_48_p1(request): input_dict = { "r1": { "bgp": { - "graceful-restart": {"graceful-restart": True,}, + "graceful-restart": { + "graceful-restart": True, + }, "address_family": { "ipv4": { "unicast": { @@ -4960,7 +5044,13 @@ def test_BGP_GR_TC_48_p1(request): step("Verify on R2 that R1 advertises GR capabilities as a restarting node") input_dict = { - "r1": {"bgp": {"graceful-restart": {"graceful-restart": True,}}}, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, "r2": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, } diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py index 6926121a6b..2ddeab13f6 100644 --- a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py @@ -456,7 +456,9 @@ def test_BGP_GR_TC_3_p0(request): input_dict = { "r1": { "bgp": { - "graceful-restart": {"disable-eor": True,}, + "graceful-restart": { + "disable-eor": True, + }, "address_family": { "ipv4": { "unicast": { @@ -2095,7 +2097,10 @@ def test_BGP_GR_chaos_33_p1(request): "ipv4": { "unicast": { "advertise_networks": [ - {"network": "200.0.20.1/32", "no_of_network": 2,} + { + "network": "200.0.20.1/32", + "no_of_network": 2, + } ] } }, @@ -2207,13 +2212,13 @@ def test_BGP_GR_chaos_33_p1(request): else: next_hop_6 = NEXT_HOP_6[1] - result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_6, - expected=False) - assert result is not True,\ - "Testcase {} :Failed \n Error {}". \ - format(tc_name, result) - logger.info(" Expected behavior: {}".\ - format(result)) + result = verify_rib( + tgen, addr_type, dut, input_dict_2, next_hop_6, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) logger.info("[Step 4] : Start BGPd daemon on R1 and R4..") @@ -3960,7 +3965,13 @@ def test_BGP_GR_21_p2(request): } } }, - "r2": {"bgp": {"graceful-restart": {"graceful-restart": True,}}}, + "r2": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + } + } + }, } configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py index 40489f438f..31fbdcd4b5 100644 --- a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py @@ -1174,6 +1174,39 @@ def test_large_community_boundary_values(request): ) +def test_large_community_invalid_chars(request): + """ + BGP canonical lcommunities must only be digits + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "1:a:2", + "large": True, + } + ] + } + } + + step("Checking boundary value for community 1:a:2") + result = create_bgp_community_lists(tgen, input_dict) + assert result is not True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + def test_large_community_after_clear_bgp(request): """ Clear BGP neighbor-ship and check if large community and community diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py index 9c0355a3e9..c2858a4bd0 100644 --- a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py @@ -2132,7 +2132,11 @@ def test_large_community_lists_with_rmap_match_regex(request): { "action": "permit", "seq_id": "10", - "match": {"large_community_list": {"id": "ALL",},}, + "match": { + "large_community_list": { + "id": "ALL", + }, + }, } ] } @@ -2208,7 +2212,11 @@ def test_large_community_lists_with_rmap_match_regex(request): { "action": "permit", "seq_id": "20", - "match": {"large_community_list": {"id": "EXP_ALL",},}, + "match": { + "large_community_list": { + "id": "EXP_ALL", + }, + }, } ] } diff --git a/tests/topotests/bgp_listen_on_multiple_addresses/bgp_listen_on_multiple_addresses.json b/tests/topotests/bgp_listen_on_multiple_addresses/bgp_listen_on_multiple_addresses.json new file mode 100644 index 0000000000..95de8cc10c --- /dev/null +++ b/tests/topotests/bgp_listen_on_multiple_addresses/bgp_listen_on_multiple_addresses.json @@ -0,0 +1,154 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "1000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "2000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "2000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "3000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py b/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py new file mode 100755 index 0000000000..d773e87ef6 --- /dev/null +++ b/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python + +# +# test_bgp_listen_on_multiple_addresses.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by Boeing Defence Australia +# Adriano Marto Reis +# +# 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_bgp_listen_on_multiple_addresses.py: Test BGP daemon listening for +connections on multiple addresses. + + +------+ +------+ +------+ +------+ + | | | | | | | | + | r1 |--------| r2 |--------| r3 |--------| r4 | + | | | | | | | | + +------+ +------+ +------+ +------+ + + | | | | + | AS 1000 | AS 2000 | AS 3000 | + | | | | + +------------+--------------------------------+-------------+ +""" + +import os +import sys +import json +import pytest + + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.common_config import start_topology +from lib.topotest import router_json_cmp, run_and_expect +from mininet.topo import Topo +from functools import partial + + +LISTEN_ADDRESSES = { + "r1": ["10.0.0.1"], + "r2": ["10.0.0.2", "10.0.1.1"], + "r3": ["10.0.1.2", "10.0.2.1"], + "r4": ["10.0.2.2"], +} + + +# Reads data from JSON File for topology and configuration creation. +jsonFile = "{}/bgp_listen_on_multiple_addresses.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + + +class TemplateTopo(Topo): + "Topology builder." + + def build(self, *_args, **_opts): + "Defines the allocation and relationship between routers and switches." + tgen = get_topogen(self) + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + "Sets up the test environment." + tgen = Topogen(TemplateTopo, mod.__name__) + + # Adds extra parameters to bgpd so they listen for connections on specific + # multiple addresses. + for router_name in tgen.routers().keys(): + tgen.net[router_name].daemons_options["bgpd"] = "-l " + " -l ".join( + LISTEN_ADDRESSES[router_name] + ) + + start_topology(tgen) + build_config_from_json(tgen, topo) + + +def teardown_module(_mod): + "Tears-down the test environment." + tgen = get_topogen() + tgen.stop_topology() + + +def test_peering(): + "Checks if the routers peer-up." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + _bgp_converge_initial("r1", "10.0.0.2") + _bgp_converge_initial("r2", "10.0.0.1") + _bgp_converge_initial("r2", "10.0.1.2") + _bgp_converge_initial("r3", "10.0.1.1") + _bgp_converge_initial("r3", "10.0.2.2") + _bgp_converge_initial("r4", "10.0.2.1") + + +def test_listening_address(): + """ + Checks if bgpd is only listening on the specified IP addresses. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for router in tgen.routers().values(): + # bgpd must not be listening on the default address. + output = router.run("netstat -nlt4 | grep 0.0.0.0:179") + assert output == "", "{}: bpgd is listening on 0.0.0.0:179".format(router.name) + + # bgpd must be listening on the specified addresses. + for address in LISTEN_ADDRESSES[router.name]: + output = router.run("netstat -nlt4 | grep {}:179".format(address)) + assert output != "", "{}: bpgd is not listening on {}:179".format( + router.name, address + ) + + +def _bgp_converge_initial(router_name, peer_address, timeout=180): + """ + Waits for the BGP connection between a given router and a given peer + (specified by its IP address) to be established. If the connection is + not established within a given timeout, then an exception is raised. + """ + tgen = get_topogen() + router = tgen.routers()[router_name] + expected = {"ipv4Unicast": {"peers": {peer_address: {"state": "Established"}}}} + + test_func = partial(router_json_cmp, router, "show ip bgp summary json", expected) + _, result = run_and_expect(test_func, None, count=timeout, wait=1) + assert result is None, "{}: Failed to establish connection with {}".format( + router_name, peer_address + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_lu_topo1/R1/bgpd.conf b/tests/topotests/bgp_lu_topo1/R1/bgpd.conf new file mode 100644 index 0000000000..1bdb4c7a3e --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R1/bgpd.conf @@ -0,0 +1,21 @@ +! +debug bgp labelpool +debug bgp zebra +! +router bgp 1 + bgp router-id 10.0.0.1 + timers bgp 3 9 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.2 remote-as 2 + neighbor 10.0.0.2 solo + neighbor 10.0.0.2 timers connect 10 +! + address-family ipv4 unicast + no neighbor 10.0.0.2 activate + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 10.0.0.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json new file mode 100644 index 0000000000..29e6c2cbf7 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json @@ -0,0 +1,8 @@ +{ + "Ledger":506, + "InUse":506, + "Requests":0, + "LabelChunks":11, + "Pending":0, + "Reconnects":0 +} diff --git a/tests/topotests/bgp_lu_topo1/R1/zebra.conf b/tests/topotests/bgp_lu_topo1/R1/zebra.conf new file mode 100644 index 0000000000..4f6fee579f --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R1/zebra.conf @@ -0,0 +1,6 @@ +debug zebra events +debug zebra dplane +debug zebra mpls +! +interface R1-eth0 + ip address 10.0.0.1/24 diff --git a/tests/topotests/bgp_lu_topo1/R2/bgpd.conf b/tests/topotests/bgp_lu_topo1/R2/bgpd.conf new file mode 100644 index 0000000000..bac608e1c3 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R2/bgpd.conf @@ -0,0 +1,23 @@ +debug bgp labelpool +debug bgp zebra +! +router bgp 2 + bgp router-id 10.0.0.2 + timers bgp 3 9 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.3 remote-as 2 + neighbor 10.0.1.3 update-source 10.0.1.2 + neighbor 10.0.1.3 timers connect 10 + neighbor 10.0.0.1 remote-as 1 + neighbor 10.0.0.1 timers connect 10 +! + address-family ipv4 unicast + neighbor 10.0.1.3 activate + no neighbor 10.0.0.1 activate + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 10.0.0.1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json new file mode 100644 index 0000000000..29e6c2cbf7 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json @@ -0,0 +1,8 @@ +{ + "Ledger":506, + "InUse":506, + "Requests":0, + "LabelChunks":11, + "Pending":0, + "Reconnects":0 +} diff --git a/tests/topotests/bgp_lu_topo1/R2/zebra.conf b/tests/topotests/bgp_lu_topo1/R2/zebra.conf new file mode 100644 index 0000000000..33ee53efe7 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R2/zebra.conf @@ -0,0 +1,11 @@ +! +debug zebra events +debug zebra dplane +debug zebra mpls +! +interface R2-eth0 + ip address 10.0.0.2/24 +! +interface R2-eth1 + ip address 10.0.1.2/24 +!
\ No newline at end of file diff --git a/tests/topotests/bgp_lu_topo1/R3/bgpd.conf b/tests/topotests/bgp_lu_topo1/R3/bgpd.conf new file mode 100644 index 0000000000..b42df022e0 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R3/bgpd.conf @@ -0,0 +1,523 @@ +log file /tmp/bgpd.log +! +debug bgp updates +! +router bgp 2 + bgp router-id 10.0.1.3 + timers bgp 3 9 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.2 remote-as 2 + neighbor 10.0.1.2 timers connect 10 + ! + address-family ipv4 unicast + neighbor 10.0.1.2 activate + network 11.0.0.1/32 + network 11.0.0.2/32 + network 11.0.0.3/32 + network 11.0.0.4/32 + network 11.0.0.5/32 + network 11.0.0.6/32 + network 11.0.0.7/32 + network 11.0.0.8/32 + network 11.0.0.9/32 + network 11.0.0.10/32 + network 11.0.0.11/32 + network 11.0.0.12/32 + network 11.0.0.13/32 + network 11.0.0.14/32 + network 11.0.0.15/32 + network 11.0.0.16/32 + network 11.0.0.17/32 + network 11.0.0.18/32 + network 11.0.0.19/32 + network 11.0.0.20/32 + network 11.0.0.21/32 + network 11.0.0.22/32 + network 11.0.0.23/32 + network 11.0.0.24/32 + network 11.0.0.25/32 + network 11.0.0.26/32 + network 11.0.0.27/32 + network 11.0.0.28/32 + network 11.0.0.29/32 + network 11.0.0.30/32 + network 11.0.0.31/32 + network 11.0.0.32/32 + network 11.0.0.33/32 + network 11.0.0.34/32 + network 11.0.0.35/32 + network 11.0.0.36/32 + network 11.0.0.37/32 + network 11.0.0.38/32 + network 11.0.0.39/32 + network 11.0.0.40/32 + network 11.0.0.41/32 + network 11.0.0.42/32 + network 11.0.0.43/32 + network 11.0.0.44/32 + network 11.0.0.45/32 + network 11.0.0.46/32 + network 11.0.0.47/32 + network 11.0.0.48/32 + network 11.0.0.49/32 + network 11.0.0.50/32 + network 11.0.0.51/32 + network 11.0.0.52/32 + network 11.0.0.53/32 + network 11.0.0.54/32 + network 11.0.0.55/32 + network 11.0.0.56/32 + network 11.0.0.57/32 + network 11.0.0.58/32 + network 11.0.0.59/32 + network 11.0.0.60/32 + network 11.0.0.61/32 + network 11.0.0.62/32 + network 11.0.0.63/32 + network 11.0.0.64/32 + network 11.0.0.65/32 + network 11.0.0.66/32 + network 11.0.0.67/32 + network 11.0.0.68/32 + network 11.0.0.69/32 + network 11.0.0.70/32 + network 11.0.0.71/32 + network 11.0.0.72/32 + network 11.0.0.73/32 + network 11.0.0.74/32 + network 11.0.0.75/32 + network 11.0.0.76/32 + network 11.0.0.77/32 + network 11.0.0.78/32 + network 11.0.0.79/32 + network 11.0.0.80/32 + network 11.0.0.81/32 + network 11.0.0.82/32 + network 11.0.0.83/32 + network 11.0.0.84/32 + network 11.0.0.85/32 + network 11.0.0.86/32 + network 11.0.0.87/32 + network 11.0.0.88/32 + network 11.0.0.89/32 + network 11.0.0.90/32 + network 11.0.0.91/32 + network 11.0.0.92/32 + network 11.0.0.93/32 + network 11.0.0.94/32 + network 11.0.0.95/32 + network 11.0.0.96/32 + network 11.0.0.97/32 + network 11.0.0.98/32 + network 11.0.0.99/32 + network 11.0.0.100/32 + network 11.0.0.101/32 + network 11.0.0.102/32 + network 11.0.0.103/32 + network 11.0.0.104/32 + network 11.0.0.105/32 + network 11.0.0.106/32 + network 11.0.0.107/32 + network 11.0.0.108/32 + network 11.0.0.109/32 + network 11.0.0.110/32 + network 11.0.0.111/32 + network 11.0.0.112/32 + network 11.0.0.113/32 + network 11.0.0.114/32 + network 11.0.0.115/32 + network 11.0.0.116/32 + network 11.0.0.117/32 + network 11.0.0.118/32 + network 11.0.0.119/32 + network 11.0.0.120/32 + network 11.0.0.121/32 + network 11.0.0.122/32 + network 11.0.0.123/32 + network 11.0.0.124/32 + network 11.0.0.125/32 + network 11.0.0.126/32 + network 11.0.0.127/32 + network 11.0.0.128/32 + network 11.0.0.129/32 + network 11.0.0.130/32 + network 11.0.0.131/32 + network 11.0.0.132/32 + network 11.0.0.133/32 + network 11.0.0.134/32 + network 11.0.0.135/32 + network 11.0.0.136/32 + network 11.0.0.137/32 + network 11.0.0.138/32 + network 11.0.0.139/32 + network 11.0.0.140/32 + network 11.0.0.141/32 + network 11.0.0.142/32 + network 11.0.0.143/32 + network 11.0.0.144/32 + network 11.0.0.145/32 + network 11.0.0.146/32 + network 11.0.0.147/32 + network 11.0.0.148/32 + network 11.0.0.149/32 + network 11.0.0.150/32 + network 11.0.0.151/32 + network 11.0.0.152/32 + network 11.0.0.153/32 + network 11.0.0.154/32 + network 11.0.0.155/32 + network 11.0.0.156/32 + network 11.0.0.157/32 + network 11.0.0.158/32 + network 11.0.0.159/32 + network 11.0.0.160/32 + network 11.0.0.161/32 + network 11.0.0.162/32 + network 11.0.0.163/32 + network 11.0.0.164/32 + network 11.0.0.165/32 + network 11.0.0.166/32 + network 11.0.0.167/32 + network 11.0.0.168/32 + network 11.0.0.169/32 + network 11.0.0.170/32 + network 11.0.0.171/32 + network 11.0.0.172/32 + network 11.0.0.173/32 + network 11.0.0.174/32 + network 11.0.0.175/32 + network 11.0.0.176/32 + network 11.0.0.177/32 + network 11.0.0.178/32 + network 11.0.0.179/32 + network 11.0.0.180/32 + network 11.0.0.181/32 + network 11.0.0.182/32 + network 11.0.0.183/32 + network 11.0.0.184/32 + network 11.0.0.185/32 + network 11.0.0.186/32 + network 11.0.0.187/32 + network 11.0.0.188/32 + network 11.0.0.189/32 + network 11.0.0.190/32 + network 11.0.0.191/32 + network 11.0.0.192/32 + network 11.0.0.193/32 + network 11.0.0.194/32 + network 11.0.0.195/32 + network 11.0.0.196/32 + network 11.0.0.197/32 + network 11.0.0.198/32 + network 11.0.0.199/32 + network 11.0.0.200/32 + network 11.0.0.201/32 + network 11.0.0.202/32 + network 11.0.0.203/32 + network 11.0.0.204/32 + network 11.0.0.205/32 + network 11.0.0.206/32 + network 11.0.0.207/32 + network 11.0.0.208/32 + network 11.0.0.209/32 + network 11.0.0.210/32 + network 11.0.0.211/32 + network 11.0.0.212/32 + network 11.0.0.213/32 + network 11.0.0.214/32 + network 11.0.0.215/32 + network 11.0.0.216/32 + network 11.0.0.217/32 + network 11.0.0.218/32 + network 11.0.0.219/32 + network 11.0.0.220/32 + network 11.0.0.221/32 + network 11.0.0.222/32 + network 11.0.0.223/32 + network 11.0.0.224/32 + network 11.0.0.225/32 + network 11.0.0.226/32 + network 11.0.0.227/32 + network 11.0.0.228/32 + network 11.0.0.229/32 + network 11.0.0.230/32 + network 11.0.0.231/32 + network 11.0.0.232/32 + network 11.0.0.233/32 + network 11.0.0.234/32 + network 11.0.0.235/32 + network 11.0.0.236/32 + network 11.0.0.237/32 + network 11.0.0.238/32 + network 11.0.0.239/32 + network 11.0.0.240/32 + network 11.0.0.241/32 + network 11.0.0.242/32 + network 11.0.0.243/32 + network 11.0.0.244/32 + network 11.0.0.245/32 + network 11.0.0.246/32 + network 11.0.0.247/32 + network 11.0.0.248/32 + network 11.0.0.249/32 + network 11.0.0.250/32 + network 11.0.0.251/32 + network 11.0.0.252/32 + network 11.0.0.253/32 + network 11.0.1.1/32 + network 11.0.1.2/32 + network 11.0.1.3/32 + network 11.0.1.4/32 + network 11.0.1.5/32 + network 11.0.1.6/32 + network 11.0.1.7/32 + network 11.0.1.8/32 + network 11.0.1.9/32 + network 11.0.1.10/32 + network 11.0.1.11/32 + network 11.0.1.12/32 + network 11.0.1.13/32 + network 11.0.1.14/32 + network 11.0.1.15/32 + network 11.0.1.16/32 + network 11.0.1.17/32 + network 11.0.1.18/32 + network 11.0.1.19/32 + network 11.0.1.20/32 + network 11.0.1.21/32 + network 11.0.1.22/32 + network 11.0.1.23/32 + network 11.0.1.24/32 + network 11.0.1.25/32 + network 11.0.1.26/32 + network 11.0.1.27/32 + network 11.0.1.28/32 + network 11.0.1.29/32 + network 11.0.1.30/32 + network 11.0.1.31/32 + network 11.0.1.32/32 + network 11.0.1.33/32 + network 11.0.1.34/32 + network 11.0.1.35/32 + network 11.0.1.36/32 + network 11.0.1.37/32 + network 11.0.1.38/32 + network 11.0.1.39/32 + network 11.0.1.40/32 + network 11.0.1.41/32 + network 11.0.1.42/32 + network 11.0.1.43/32 + network 11.0.1.44/32 + network 11.0.1.45/32 + network 11.0.1.46/32 + network 11.0.1.47/32 + network 11.0.1.48/32 + network 11.0.1.49/32 + network 11.0.1.50/32 + network 11.0.1.51/32 + network 11.0.1.52/32 + network 11.0.1.53/32 + network 11.0.1.54/32 + network 11.0.1.55/32 + network 11.0.1.56/32 + network 11.0.1.57/32 + network 11.0.1.58/32 + network 11.0.1.59/32 + network 11.0.1.60/32 + network 11.0.1.61/32 + network 11.0.1.62/32 + network 11.0.1.63/32 + network 11.0.1.64/32 + network 11.0.1.65/32 + network 11.0.1.66/32 + network 11.0.1.67/32 + network 11.0.1.68/32 + network 11.0.1.69/32 + network 11.0.1.70/32 + network 11.0.1.71/32 + network 11.0.1.72/32 + network 11.0.1.73/32 + network 11.0.1.74/32 + network 11.0.1.75/32 + network 11.0.1.76/32 + network 11.0.1.77/32 + network 11.0.1.78/32 + network 11.0.1.79/32 + network 11.0.1.80/32 + network 11.0.1.81/32 + network 11.0.1.82/32 + network 11.0.1.83/32 + network 11.0.1.84/32 + network 11.0.1.85/32 + network 11.0.1.86/32 + network 11.0.1.87/32 + network 11.0.1.88/32 + network 11.0.1.89/32 + network 11.0.1.90/32 + network 11.0.1.91/32 + network 11.0.1.92/32 + network 11.0.1.93/32 + network 11.0.1.94/32 + network 11.0.1.95/32 + network 11.0.1.96/32 + network 11.0.1.97/32 + network 11.0.1.98/32 + network 11.0.1.99/32 + network 11.0.1.100/32 + network 11.0.1.101/32 + network 11.0.1.102/32 + network 11.0.1.103/32 + network 11.0.1.104/32 + network 11.0.1.105/32 + network 11.0.1.106/32 + network 11.0.1.107/32 + network 11.0.1.108/32 + network 11.0.1.109/32 + network 11.0.1.110/32 + network 11.0.1.111/32 + network 11.0.1.112/32 + network 11.0.1.113/32 + network 11.0.1.114/32 + network 11.0.1.115/32 + network 11.0.1.116/32 + network 11.0.1.117/32 + network 11.0.1.118/32 + network 11.0.1.119/32 + network 11.0.1.120/32 + network 11.0.1.121/32 + network 11.0.1.122/32 + network 11.0.1.123/32 + network 11.0.1.124/32 + network 11.0.1.125/32 + network 11.0.1.126/32 + network 11.0.1.127/32 + network 11.0.1.128/32 + network 11.0.1.129/32 + network 11.0.1.130/32 + network 11.0.1.131/32 + network 11.0.1.132/32 + network 11.0.1.133/32 + network 11.0.1.134/32 + network 11.0.1.135/32 + network 11.0.1.136/32 + network 11.0.1.137/32 + network 11.0.1.138/32 + network 11.0.1.139/32 + network 11.0.1.140/32 + network 11.0.1.141/32 + network 11.0.1.142/32 + network 11.0.1.143/32 + network 11.0.1.144/32 + network 11.0.1.145/32 + network 11.0.1.146/32 + network 11.0.1.147/32 + network 11.0.1.148/32 + network 11.0.1.149/32 + network 11.0.1.150/32 + network 11.0.1.151/32 + network 11.0.1.152/32 + network 11.0.1.153/32 + network 11.0.1.154/32 + network 11.0.1.155/32 + network 11.0.1.156/32 + network 11.0.1.157/32 + network 11.0.1.158/32 + network 11.0.1.159/32 + network 11.0.1.160/32 + network 11.0.1.161/32 + network 11.0.1.162/32 + network 11.0.1.163/32 + network 11.0.1.164/32 + network 11.0.1.165/32 + network 11.0.1.166/32 + network 11.0.1.167/32 + network 11.0.1.168/32 + network 11.0.1.169/32 + network 11.0.1.170/32 + network 11.0.1.171/32 + network 11.0.1.172/32 + network 11.0.1.173/32 + network 11.0.1.174/32 + network 11.0.1.175/32 + network 11.0.1.176/32 + network 11.0.1.177/32 + network 11.0.1.178/32 + network 11.0.1.179/32 + network 11.0.1.180/32 + network 11.0.1.181/32 + network 11.0.1.182/32 + network 11.0.1.183/32 + network 11.0.1.184/32 + network 11.0.1.185/32 + network 11.0.1.186/32 + network 11.0.1.187/32 + network 11.0.1.188/32 + network 11.0.1.189/32 + network 11.0.1.190/32 + network 11.0.1.191/32 + network 11.0.1.192/32 + network 11.0.1.193/32 + network 11.0.1.194/32 + network 11.0.1.195/32 + network 11.0.1.196/32 + network 11.0.1.197/32 + network 11.0.1.198/32 + network 11.0.1.199/32 + network 11.0.1.200/32 + network 11.0.1.201/32 + network 11.0.1.202/32 + network 11.0.1.203/32 + network 11.0.1.204/32 + network 11.0.1.205/32 + network 11.0.1.206/32 + network 11.0.1.207/32 + network 11.0.1.208/32 + network 11.0.1.209/32 + network 11.0.1.210/32 + network 11.0.1.211/32 + network 11.0.1.212/32 + network 11.0.1.213/32 + network 11.0.1.214/32 + network 11.0.1.215/32 + network 11.0.1.216/32 + network 11.0.1.217/32 + network 11.0.1.218/32 + network 11.0.1.219/32 + network 11.0.1.220/32 + network 11.0.1.221/32 + network 11.0.1.222/32 + network 11.0.1.223/32 + network 11.0.1.224/32 + network 11.0.1.225/32 + network 11.0.1.226/32 + network 11.0.1.227/32 + network 11.0.1.228/32 + network 11.0.1.229/32 + network 11.0.1.230/32 + network 11.0.1.231/32 + network 11.0.1.232/32 + network 11.0.1.233/32 + network 11.0.1.234/32 + network 11.0.1.235/32 + network 11.0.1.236/32 + network 11.0.1.237/32 + network 11.0.1.238/32 + network 11.0.1.239/32 + network 11.0.1.240/32 + network 11.0.1.241/32 + network 11.0.1.242/32 + network 11.0.1.243/32 + network 11.0.1.244/32 + network 11.0.1.245/32 + network 11.0.1.246/32 + network 11.0.1.247/32 + network 11.0.1.248/32 + network 11.0.1.249/32 + network 11.0.1.250/32 + network 11.0.1.251/32 + network 11.0.1.252/32 + network 11.0.1.253/32 + exit-address-family + ! +! diff --git a/tests/topotests/bgp_lu_topo1/R3/zebra.conf b/tests/topotests/bgp_lu_topo1/R3/zebra.conf new file mode 100644 index 0000000000..524978bff6 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/R3/zebra.conf @@ -0,0 +1,9 @@ +log file /tmp/zebra.log +! +debug zebra events +debug zebra packet detail +debug zebra mpls +! +interface R3-eth0 + ip address 10.0.1.3/24 +! diff --git a/tests/topotests/bgp_lu_topo1/test_bgp_lu.py b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py new file mode 100644 index 0000000000..61418d7a79 --- /dev/null +++ b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python + +# +# test_bgp_lu.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# 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_bgp_lu.py: Test BGP LU label allocation +""" + +import os +import sys +import json +from functools import partial +from time import sleep +import pytest + +# 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 + +#Basic scenario for BGP-LU. Nodes are directly connected. +#Node 3 is advertising many routes to 2, which advertises them +#as BGP-LU to 1; this way we get routes with actual labels, as +#opposed to implicit-null routes in the 2-node case. +# +# AS1 BGP-LU AS2 iBGP AS2 +#+-----+ +-----+ +-----+ +#| |.1 .2| |.2 .3| | +#| 1 +----------------+ 2 +-----------------+ 3 | +#| | 10.0.0.0/24 | | 10.0.1.0/24 | | +#+-----+ +-----+ +-----+ + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("R1") + tgen.add_router("R2") + tgen.add_router("R3") + + # R1-R2 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R2"]) + + # R2-R3 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["R2"]) + switch.add_link(tgen.gears["R3"]) + + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + +def check_labelpool(router): + json_file = "{}/{}/labelpool.summ.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(topotest.router_json_cmp, router, "show bgp labelpool summary json", expected) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"{}" JSON output mismatches - Did not converge'.format(router.name) + assert result is None, assertmsg + +def test_converge_bgplu(): + "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) + + #tgen.mininet_cli(); + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + + check_labelpool(r1) + check_labelpool(r2) + +def test_clear_bgplu(): + "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) + + #tgen.mininet_cli(); + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + + r1.vtysh_cmd("clear bgp 10.0.0.2") + check_labelpool(r1) + check_labelpool(r2) + + r2.vtysh_cmd("clear bgp 10.0.1.3") + check_labelpool(r1) + check_labelpool(r2) + + r1.vtysh_cmd("clear bgp 10.0.0.2") + r2.vtysh_cmd("clear bgp 10.0.1.3") + check_labelpool(r1) + check_labelpool(r2) + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py index 9703cf8d57..19a9140c13 100644 --- a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py +++ b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py @@ -95,7 +95,7 @@ from lib.common_config import ( kill_router_daemons, start_router_daemons, stop_router, - start_router + start_router, ) from lib.topolog import logger @@ -129,7 +129,7 @@ LOOPBACK_2 = { "ipv4": "20.20.20.20/32", "ipv6": "20::20:20/128", "ipv4_mask": "255.255.255.255", - "ipv6_mask": None + "ipv6_mask": None, } MAX_PATHS = 2 @@ -724,16 +724,40 @@ def test_vrf_with_multiple_links_p1(request): "local_as": "200", "vrf": "RED_A", "address_family": { - "ipv4": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, - "ipv6": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": MAX_PATHS, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": MAX_PATHS, + } + } + }, }, }, { "local_as": "200", "vrf": "BLUE_A", "address_family": { - "ipv4": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, - "ipv6": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": MAX_PATHS, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": MAX_PATHS, + } + } + }, }, }, ] @@ -2148,7 +2172,7 @@ def test_restart_bgpd_daemon_p1(request): assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) result = verify_bgp_convergence(tgen, topo) - assert result is True, "Testcase () :Failed\n Error {}". format(tc_name, result) + assert result is True, "Testcase () :Failed\n Error {}".format(tc_name, result) step("Kill BGPd daemon on R1.") kill_router_daemons(tgen, "r1", ["bgpd"]) @@ -2663,6 +2687,9 @@ def test_delete_and_re_add_vrf_p1(request): } } + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {}: Failed\n Error {}".format(tc_name, result) + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) diff --git a/tests/topotests/bgp_peer-group/__init__.py b/tests/topotests/bgp_peer-group/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_peer-group/__init__.py diff --git a/tests/topotests/bgp_peer-group/r1/bgpd.conf b/tests/topotests/bgp_peer-group/r1/bgpd.conf new file mode 100644 index 0000000000..19b490a359 --- /dev/null +++ b/tests/topotests/bgp_peer-group/r1/bgpd.conf @@ -0,0 +1,8 @@ +! +router bgp 65001 + neighbor PG peer-group + neighbor PG remote-as external + neighbor PG timers 3 10 + neighbor 192.168.255.3 peer-group PG + neighbor r1-eth0 interface peer-group PG +! diff --git a/tests/topotests/bgp_peer-group/r1/zebra.conf b/tests/topotests/bgp_peer-group/r1/zebra.conf new file mode 100644 index 0000000000..e2c399e536 --- /dev/null +++ b/tests/topotests/bgp_peer-group/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_peer-group/r2/bgpd.conf b/tests/topotests/bgp_peer-group/r2/bgpd.conf new file mode 100644 index 0000000000..0880ee9fae --- /dev/null +++ b/tests/topotests/bgp_peer-group/r2/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65002 + neighbor PG peer-group + neighbor PG remote-as external + neighbor PG timers 3 10 + neighbor r2-eth0 interface peer-group PG +! diff --git a/tests/topotests/bgp_peer-group/r2/zebra.conf b/tests/topotests/bgp_peer-group/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_peer-group/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_peer-group/r3/bgpd.conf b/tests/topotests/bgp_peer-group/r3/bgpd.conf new file mode 100644 index 0000000000..eb2fca15fb --- /dev/null +++ b/tests/topotests/bgp_peer-group/r3/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65003 + neighbor PG peer-group + neighbor PG remote-as external + neighbor PG timers 3 10 + neighbor 192.168.255.1 peer-group PG +! diff --git a/tests/topotests/bgp_peer-group/r3/zebra.conf b/tests/topotests/bgp_peer-group/r3/zebra.conf new file mode 100644 index 0000000000..e9fdfb70c5 --- /dev/null +++ b/tests/topotests/bgp_peer-group/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.255.3/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_peer-group/test_bgp_peer-group.py b/tests/topotests/bgp_peer-group/test_bgp_peer-group.py new file mode 100644 index 0000000000..7c7a8b87ed --- /dev/null +++ b/tests/topotests/bgp_peer-group/test_bgp_peer-group.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# 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 if peer-group works for numbered and unnumbered configurations. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_peer_group(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_peer_group_configured(): + output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor json")) + expected = { + "r1-eth0": {"peerGroup": "PG", "bgpState": "Established"}, + "192.168.255.3": {"peerGroup": "PG", "bgpState": "Established"}, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_peer_group_configured) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r1"]) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py index 6d7131e1e5..ceac84709b 100644 --- a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py +++ b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py @@ -101,7 +101,11 @@ def test_r1_receive_and_advertise_prefix_sid_type1(): "prefix": prefix, "advertisedTo": {"10.0.0.101": {}, "10.0.0.102": {}}, "paths": [ - {"valid": True, "remoteLabel": remoteLabel, "labelIndex": labelIndex,} + { + "valid": True, + "remoteLabel": remoteLabel, + "labelIndex": labelIndex, + } ], } return topotest.json_cmp(output, expected) diff --git a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py index 3af944473d..3f9009967d 100644 --- a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py +++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py @@ -1042,9 +1042,10 @@ def test_next_hop_with_recursive_lookup_p1(request): next_hop=next_hop, expected=False, ) - assert result is not True, ( - "Testcase {} : Failed \n " - "Route is still present \n Error : {}".format(tc_name, result) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Route is still present \n Error : {}".format( + tc_name, result ) step("Re-apply redistribution on R4.") @@ -1125,9 +1126,10 @@ def test_next_hop_with_recursive_lookup_p1(request): next_hop=next_hop, expected=False, ) - assert result is not True, ( - "Testcase {} : Failed \n " - "Route is still present \n Error : {}".format(tc_name, result) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Route is still present \n Error : {}".format( + tc_name, result ) shutdown_bringup_interface(tgen, "r3", intf_r3_r4, True) @@ -1182,9 +1184,10 @@ def test_next_hop_with_recursive_lookup_p1(request): next_hop=next_hop, expected=False, ) - assert result is not True, ( - "Testcase {} : Failed \n " - "Route is still present \n Error : {}".format(tc_name, result) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Route is still present \n Error : {}".format( + tc_name, result ) shutdown_bringup_interface(tgen, "r4", intf_r4_r3, True) @@ -2101,8 +2104,20 @@ def test_BGP_active_standby_preemption_and_ecmp_p1(request): "r4": { "bgp": { "address_family": { - "ipv4": {"unicast": {"maximum_paths": {"ebgp": 1,}}}, - "ipv6": {"unicast": {"maximum_paths": {"ebgp": 1,}}}, + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": 1, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": 1, + } + } + }, } } } @@ -2131,8 +2146,20 @@ def test_BGP_active_standby_preemption_and_ecmp_p1(request): "r4": { "bgp": { "address_family": { - "ipv4": {"unicast": {"maximum_paths": {"ebgp": 2,}}}, - "ipv6": {"unicast": {"maximum_paths": {"ebgp": 2,}}}, + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": 2, + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": 2, + } + } + }, } } } diff --git a/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py index 0fabd90341..0467bf1bfb 100644 --- a/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py +++ b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py @@ -415,9 +415,10 @@ def test_route_summarisation_with_summary_only_p1(request): result = verify_rib( tgen, addr_type, "r3", input_static, protocol="bgp", expected=False ) - assert result is not True, ( - "Testcase : Failed \n " - "Routes are still present \n Error: {}".format(tc_name, result) + assert ( + result is not True + ), "Testcase : Failed \n " "Routes are still present \n Error: {}".format( + tc_name, result ) result = verify_rib(tgen, addr_type, "r1", input_static_agg, protocol="bgp") @@ -614,7 +615,9 @@ def test_route_summarisation_with_summary_only_p1(request): addr_type: { "unicast": { "advertise_networks": [ - {"network": NETWORK_4_1[addr_type],} + { + "network": NETWORK_4_1[addr_type], + } ] } } @@ -1014,7 +1017,11 @@ def test_route_summarisation_with_as_set_p1(request): assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) for addr_type in ADDR_TYPES: - for pfx, seq_id, network, in zip([6, 7], [60, 70], [NETWORK_3_1, NETWORK_4_1]): + for ( + pfx, + seq_id, + network, + ) in zip([6, 7], [60, 70], [NETWORK_3_1, NETWORK_4_1]): prefix_list = { "r1": { "prefix_lists": { diff --git a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py index cf8be5f44f..c75055c26f 100644 --- a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py +++ b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py @@ -56,6 +56,7 @@ class TemplateTopo(Topo): switch.add_link(tgen.gears["r2"]) switch.add_link(tgen.gears["r3"]) + def setup_module(mod): tgen = Topogen(TemplateTopo, mod.__name__) tgen.start_topology() @@ -114,6 +115,7 @@ def test_bgp_route(): assertmsg = '"r3" JSON output mismatches' assert result is None, assertmsg + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py index 9106c163cd..6e7495d929 100644 --- a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py @@ -943,9 +943,10 @@ def test_modify_route_map_match_set_clauses_p1(request): } result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False) - assert result is not True, ( - "Testcase {} : Failed \n Error : Routes are still " - "present {}".format(tc_name, result) + assert ( + result is not True + ), "Testcase {} : Failed \n Error : Routes are still " "present {}".format( + tc_name, result ) write_test_footer(tc_name) diff --git a/tests/topotests/eigrp-topo1/test_eigrp_topo1.py b/tests/topotests/eigrp-topo1/test_eigrp_topo1.py index 3ce1472ac0..bf94d39a4b 100644 --- a/tests/topotests/eigrp-topo1/test_eigrp_topo1.py +++ b/tests/topotests/eigrp-topo1/test_eigrp_topo1.py @@ -91,7 +91,7 @@ class NetworkTopo(Topo): ## ##################################################### - +@pytest.mark.eigrp def setup_module(module): "Setup topology" tgen = Topogen(NetworkTopo, module.__name__) diff --git a/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py b/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py index 265124132f..d8c0cdc2fd 100644 --- a/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py +++ b/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py @@ -34,6 +34,8 @@ import pytest import json from functools import partial +pytestmark = pytest.mark.pimd + # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(CWD, "../")) diff --git a/tests/topotests/example-test/test_template.py b/tests/topotests/example-test/test_template.py index 4305e0199f..973303b830 100644 --- a/tests/topotests/example-test/test_template.py +++ b/tests/topotests/example-test/test_template.py @@ -44,6 +44,18 @@ from lib.topolog import logger from mininet.topo import Topo +#TODO: select markers based on daemons used during test +# pytest module level markers +""" +pytestmark = pytest.mark.bfdd # single marker +pytestmark = [ + pytest.mark.bgpd, + pytest.mark.ospfd, + pytest.mark.ospf6d +] # multiple markers +""" + + class TemplateTopo(Topo): "Test topology builder" diff --git a/tests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py b/tests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py index f24f463b8a..cd48716905 100755 --- a/tests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py +++ b/tests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py @@ -53,6 +53,19 @@ from lib.topolog import logger from lib.bgp import verify_bgp_convergence from lib.topojson import build_topo_from_json, build_config_from_json + +#TODO: select markers based on daemons used during test +# pytest module level markers +""" +pytestmark = pytest.mark.bfdd # single marker +pytestmark = [ + pytest.mark.bgpd, + pytest.mark.ospfd, + pytest.mark.ospf6d +] # multiple markers +""" + + # Reading the data from JSON File for topology and configuration creation jsonFile = "{}/example_topojson_multiple_links.json".format(CWD) try: diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py b/tests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py index 3ae3c9f4fe..0c72e30044 100755 --- a/tests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py +++ b/tests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py @@ -52,6 +52,19 @@ from lib.topolog import logger from lib.bgp import verify_bgp_convergence from lib.topojson import build_topo_from_json, build_config_from_json + +#TODO: select markers based on daemons used during test +# pytest module level markers +""" +pytestmark = pytest.mark.bfdd # single marker +pytestmark = [ + pytest.mark.bgpd, + pytest.mark.ospfd, + pytest.mark.ospf6d +] # multiple markers +""" + + # Reading the data from JSON File for topology and configuration creation jsonFile = "{}/example_topojson.json".format(CWD) diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py index 06fa2f4626..d05ad6db21 100755 --- a/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py +++ b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py @@ -54,6 +54,19 @@ from lib.topolog import logger from lib.bgp import verify_bgp_convergence from lib.topojson import build_topo_from_json, build_config_from_json + +#TODO: select markers based on daemons used during test +# pytest module level markers +""" +pytestmark = pytest.mark.bfdd # single marker +pytestmark = [ + pytest.mark.bgpd, + pytest.mark.ospfd, + pytest.mark.ospf6d +] # multiple markers +""" + + # Reading the data from JSON File for topology and configuration creation jsonFile = "{}/example_topojson.json".format(CWD) diff --git a/tests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py b/tests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py index 67edcae90e..a655b418cf 100755 --- a/tests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py +++ b/tests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py @@ -62,7 +62,7 @@ from functools import partial # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers @@ -76,8 +76,10 @@ from mininet.topo import Topo # Global multi-dimensional dictionary containing all expected outputs outputs = {} + class TemplateTopo(Topo): "Test topology builder" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) @@ -85,59 +87,58 @@ class TemplateTopo(Topo): # # Define FRR Routers # - for router in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'rt7']: + for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]: tgen.add_router(router) # # Define connections # - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['rt1'], nodeif="eth-rt2") - switch.add_link(tgen.gears['rt2'], nodeif="eth-rt1") - switch = tgen.add_switch('s2') - switch.add_link(tgen.gears['rt2'], nodeif="eth-rt3") - switch.add_link(tgen.gears['rt3'], nodeif="eth-rt2") - switch = tgen.add_switch('s3') - switch.add_link(tgen.gears['rt1'], nodeif="eth-rt3") - switch.add_link(tgen.gears['rt3'], nodeif="eth-rt1") - switch = tgen.add_switch('s4') - switch.add_link(tgen.gears['rt1'], nodeif="eth-rt4") - switch.add_link(tgen.gears['rt4'], nodeif="eth-rt1") - switch = tgen.add_switch('s5') - switch.add_link(tgen.gears['rt1'], nodeif="eth-rt5") - switch.add_link(tgen.gears['rt5'], nodeif="eth-rt1") - switch = tgen.add_switch('s6') - switch.add_link(tgen.gears['rt1'], nodeif="eth-rt6") - switch.add_link(tgen.gears['rt6'], nodeif="eth-rt1") - switch = tgen.add_switch('s7') - switch.add_link(tgen.gears['rt2'], nodeif="eth-rt7") - switch.add_link(tgen.gears['rt7'], nodeif="eth-rt2") - switch = tgen.add_switch('s8') - switch.add_link(tgen.gears['rt3'], nodeif="eth-rt7") - switch.add_link(tgen.gears['rt7'], nodeif="eth-rt3") - switch = tgen.add_switch('s9') - switch.add_link(tgen.gears['rt4'], nodeif="eth-rt7") - switch.add_link(tgen.gears['rt7'], nodeif="eth-rt4") - switch = tgen.add_switch('s10') - switch.add_link(tgen.gears['rt5'], nodeif="eth-rt7") - switch.add_link(tgen.gears['rt7'], nodeif="eth-rt5") - switch = tgen.add_switch('s11') - switch.add_link(tgen.gears['rt6'], nodeif="eth-rt7") - switch.add_link(tgen.gears['rt7'], nodeif="eth-rt6") + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1") + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2") + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1") + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt4") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt1") + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt1") + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt6") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt1") + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt7") + switch.add_link(tgen.gears["rt7"], nodeif="eth-rt2") + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt7") + switch.add_link(tgen.gears["rt7"], nodeif="eth-rt3") + switch = tgen.add_switch("s9") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt7") + switch.add_link(tgen.gears["rt7"], nodeif="eth-rt4") + switch = tgen.add_switch("s10") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt7") + switch.add_link(tgen.gears["rt7"], nodeif="eth-rt5") + switch = tgen.add_switch("s11") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt7") + switch.add_link(tgen.gears["rt7"], nodeif="eth-rt6") # # Populate multi-dimensional dictionary containing all expected outputs # - files = ["show_ipv6_route.ref", - "show_yang_interface_isis_adjacencies.ref"] - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'rt7']: + files = ["show_ipv6_route.ref", "show_yang_interface_isis_adjacencies.ref"] + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]: outputs[rname] = {} for step in range(1, 13 + 1): outputs[rname][step] = {} for file in files: if step == 1: # Get snapshots relative to the expected initial network convergence - filename = '{}/{}/step{}/{}'.format(CWD, rname, step, file) + filename = "{}/{}/step{}/{}".format(CWD, rname, step, file) outputs[rname][step][file] = open(filename).read() else: if rname != "rt1": @@ -146,20 +147,23 @@ class TemplateTopo(Topo): continue # Get diff relative to the previous step - filename = '{}/{}/step{}/{}.diff'.format(CWD, rname, step, file) + filename = "{}/{}/step{}/{}.diff".format(CWD, rname, step, file) # Create temporary files in order to apply the diff f_in = tempfile.NamedTemporaryFile() f_in.write(outputs[rname][step - 1][file]) f_in.flush() f_out = tempfile.NamedTemporaryFile() - os.system("patch -s -o %s %s %s" %(f_out.name, f_in.name, filename)) + os.system( + "patch -s -o %s %s %s" % (f_out.name, f_in.name, filename) + ) # Store the updated snapshot and remove the temporary files outputs[rname][step][file] = open(f_out.name).read() f_in.close() f_out.close() +@pytest.mark.isis def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(TemplateTopo, mod.__name__) @@ -170,16 +174,15 @@ def setup_module(mod): # For all registered routers, load the zebra configuration file for rname, router in router_list.iteritems(): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_ISIS, - os.path.join(CWD, '{}/isisd.conf'.format(rname)) + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) ) tgen.start_router() + def teardown_module(mod): "Teardown the pytest environment" tgen = get_topogen() @@ -187,6 +190,7 @@ def teardown_module(mod): # This function tears down the whole topology. tgen.stop_topology() + def router_compare_json_output(rname, command, reference): "Compare router JSON output" @@ -196,12 +200,12 @@ def router_compare_json_output(rname, command, reference): expected = json.loads(reference) # Run test function until we get an result. Wait at most 60 seconds. - test_func = partial(topotest.router_json_cmp, - tgen.gears[rname], command, expected) + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5) assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) assert diff is None, assertmsg + # # Step 1 # @@ -215,9 +219,13 @@ def test_isis_adjacencies_step1(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'rt7']: - router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", - outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]: + router_compare_json_output( + rname, + "show yang operational-data /frr-interface:lib isisd", + outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"], + ) + def test_rib_ipv6_step1(): logger.info("Test (step 1): verify IPv6 RIB") @@ -227,9 +235,11 @@ def test_rib_ipv6_step1(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'rt7']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][1]["show_ipv6_route.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][1]["show_ipv6_route.ref"] + ) + # # Step 2 @@ -248,16 +258,28 @@ def test_rib_ipv6_step2(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Disabling LFA protection on all rt1 interfaces') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute lfa"') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt3" -c "no isis fast-reroute lfa"') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt4" -c "no isis fast-reroute lfa"') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "no isis fast-reroute lfa"') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt6" -c "no isis fast-reroute lfa"') + logger.info("Disabling LFA protection on all rt1 interfaces") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute lfa"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt3" -c "no isis fast-reroute lfa"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt4" -c "no isis fast-reroute lfa"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt5" -c "no isis fast-reroute lfa"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt6" -c "no isis fast-reroute lfa"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][2]["show_ipv6_route.ref"] + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][2]["show_ipv6_route.ref"]) # # Step 3 @@ -276,16 +298,28 @@ def test_rib_ipv6_step3(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Re-enabling LFA protection on all rt1 interfaces') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute lfa"') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt3" -c "isis fast-reroute lfa"') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt4" -c "isis fast-reroute lfa"') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "isis fast-reroute lfa"') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt6" -c "isis fast-reroute lfa"') + logger.info("Re-enabling LFA protection on all rt1 interfaces") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute lfa"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt3" -c "isis fast-reroute lfa"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt4" -c "isis fast-reroute lfa"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt5" -c "isis fast-reroute lfa"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt6" -c "isis fast-reroute lfa"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][3]["show_ipv6_route.ref"] + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][3]["show_ipv6_route.ref"]) # # Step 4 @@ -304,12 +338,16 @@ def test_rib_ipv6_step4(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Disabling LFA load-sharing on rt1') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "fast-reroute load-sharing disable"') + logger.info("Disabling LFA load-sharing on rt1") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute load-sharing disable"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][4]["show_ipv6_route.ref"] + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][4]["show_ipv6_route.ref"]) # # Step 5 @@ -328,12 +366,16 @@ def test_rib_ipv6_step5(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Re-enabling LFA load-sharing on rt1') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no fast-reroute load-sharing disable"') + logger.info("Re-enabling LFA load-sharing on rt1") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "no fast-reroute load-sharing disable"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][5]["show_ipv6_route.ref"] + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][5]["show_ipv6_route.ref"]) # # Step 6 @@ -352,12 +394,16 @@ def test_rib_ipv6_step6(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Limiting backup computation to critical priority prefixes only') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "fast-reroute priority-limit critical"') + logger.info("Limiting backup computation to critical priority prefixes only") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute priority-limit critical"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][6]["show_ipv6_route.ref"] + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][6]["show_ipv6_route.ref"]) # # Step 7 @@ -377,13 +423,19 @@ def test_rib_ipv6_step7(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Configuring a prefix priority list') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "spf prefix-priority critical CRITICAL_DESTINATIONS"') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "ipv6 access-list CRITICAL_DESTINATIONS seq 5 permit 2001:db8:1000::7/128"') + logger.info("Configuring a prefix priority list") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "spf prefix-priority critical CRITICAL_DESTINATIONS"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "ipv6 access-list CRITICAL_DESTINATIONS seq 5 permit 2001:db8:1000::7/128"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][7]["show_ipv6_route.ref"] + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][7]["show_ipv6_route.ref"]) # # Step 8 @@ -402,14 +454,22 @@ def test_rib_ipv6_step8(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Reverting previous changes related to prefix priorities') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "no ipv6 access-list CRITICAL_DESTINATIONS seq 5 permit 2001:db8:1000::7/128"') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no fast-reroute priority-limit critical"') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no spf prefix-priority critical CRITICAL_DESTINATIONS"') + logger.info("Reverting previous changes related to prefix priorities") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "no ipv6 access-list CRITICAL_DESTINATIONS seq 5 permit 2001:db8:1000::7/128"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "no fast-reroute priority-limit critical"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "no spf prefix-priority critical CRITICAL_DESTINATIONS"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][8]["show_ipv6_route.ref"] + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][8]["show_ipv6_route.ref"]) # # Step 9 @@ -428,12 +488,16 @@ def test_rib_ipv6_step9(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Excluding eth-rt6 from LFA computation for eth-rt2\'s failure') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute lfa exclude interface eth-rt6"') + logger.info("Excluding eth-rt6 from LFA computation for eth-rt2's failure") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute lfa exclude interface eth-rt6"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][9]["show_ipv6_route.ref"] + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][9]["show_ipv6_route.ref"]) # # Step 10 @@ -452,12 +516,20 @@ def test_rib_ipv6_step10(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Removing exclusion of eth-rt6 from LFA computation for eth-rt2\'s failure') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute lfa exclude interface eth-rt6"') + logger.info( + "Removing exclusion of eth-rt6 from LFA computation for eth-rt2's failure" + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute lfa exclude interface eth-rt6"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, + "show ipv6 route isis json", + outputs[rname][10]["show_ipv6_route.ref"], + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][10]["show_ipv6_route.ref"]) # # Step 11 @@ -476,12 +548,18 @@ def test_rib_ipv6_step11(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Adding LFA tiebreaker: prefer node protecting backup path') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker node-protecting index 10"') + logger.info("Adding LFA tiebreaker: prefer node protecting backup path") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker node-protecting index 10"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, + "show ipv6 route isis json", + outputs[rname][11]["show_ipv6_route.ref"], + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][11]["show_ipv6_route.ref"]) # # Step 12 @@ -500,12 +578,18 @@ def test_rib_ipv6_step12(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Adding LFA tiebreaker: prefer backup path via downstream node') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker downstream index 20"') + logger.info("Adding LFA tiebreaker: prefer backup path via downstream node") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker downstream index 20"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, + "show ipv6 route isis json", + outputs[rname][12]["show_ipv6_route.ref"], + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][12]["show_ipv6_route.ref"]) # # Step 13 @@ -524,22 +608,29 @@ def test_rib_ipv6_step13(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Adding LFA tiebreaker: prefer backup path with lowest total metric') - tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker lowest-backup-metric index 30"') + logger.info("Adding LFA tiebreaker: prefer backup path with lowest total metric") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker lowest-backup-metric index 30"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, + "show ipv6 route isis json", + outputs[rname][13]["show_ipv6_route.ref"], + ) - for rname in ['rt1']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][13]["show_ipv6_route.ref"]) # Memory leak test template def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') + pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() -if __name__ == '__main__': + +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis-lsp-bits-topo1/__init__.py b/tests/topotests/isis-lsp-bits-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/__init__.py diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/isisd.conf b/tests/topotests/isis-lsp-bits-topo1/rt1/isisd.conf new file mode 100644 index 0000000000..90764a0d0f --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt1/isisd.conf @@ -0,0 +1,27 @@ +password 1 +hostname rt1 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis priority 100 +! +router isis 1 + net 49.0000.0000.0000.0001.00 + is-type level-1 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_ip_route.ref new file mode 100644 index 0000000000..8557f4b010 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_ip_route.ref @@ -0,0 +1,89 @@ +{ + "0.0.0.0\/0":[ + { + "prefix":"0.0.0.0\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..fa76533756 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_ipv6_route.ref @@ -0,0 +1,65 @@ +{ + "::\/0":[ + { + "prefix":"::\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..26f0dffa7a --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,32 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-sw1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 64, + "state": "up" + }, + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 64, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/step2/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt1/step2/show_ip_route.ref new file mode 100644 index 0000000000..c826efdcfe --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt1/step2/show_ip_route.ref @@ -0,0 +1,82 @@ +{ + "0.0.0.0\/0":[ + { + "prefix":"0.0.0.0\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/step2/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt1/step2/show_ipv6_route.ref new file mode 100644 index 0000000000..a386b45dad --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt1/step2/show_ipv6_route.ref @@ -0,0 +1,59 @@ +{ + "::\/0":[ + { + "prefix":"::\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/step3/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt1/step3/show_ip_route.ref new file mode 100644 index 0000000000..2b281b74fb --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt1/step3/show_ip_route.ref @@ -0,0 +1,62 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/step3/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt1/step3/show_ipv6_route.ref new file mode 100644 index 0000000000..4b920eda01 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt1/step3/show_ipv6_route.ref @@ -0,0 +1,40 @@ +{ + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/step4/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt1/step4/show_ip_route.ref new file mode 100644 index 0000000000..8557f4b010 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt1/step4/show_ip_route.ref @@ -0,0 +1,89 @@ +{ + "0.0.0.0\/0":[ + { + "prefix":"0.0.0.0\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/step4/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt1/step4/show_ipv6_route.ref new file mode 100644 index 0000000000..fa76533756 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt1/step4/show_ipv6_route.ref @@ -0,0 +1,65 @@ +{ + "::\/0":[ + { + "prefix":"::\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/zebra.conf b/tests/topotests/isis-lsp-bits-topo1/rt1/zebra.conf new file mode 100644 index 0000000000..9d71d3005f --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt1/zebra.conf @@ -0,0 +1,19 @@ +log file zebra.log +! +hostname rt1 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 + ipv6 address 2001:db8:1000::1/128 +! +interface eth-sw1 + ip address 10.0.1.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-lsp-bits-topo1/rt2/isisd.conf b/tests/topotests/isis-lsp-bits-topo1/rt2/isisd.conf new file mode 100644 index 0000000000..2bc4c4ad97 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt2/isisd.conf @@ -0,0 +1,35 @@ +hostname rt2 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt4 + ip router isis 2 + ipv6 router isis 2 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0002.00 + lsp-gen-interval 2 + topology ipv6-unicast +! +router isis 2 + net 49.0002.0000.0000.0002.00 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_ip_route.ref new file mode 100644 index 0000000000..d7e886ce86 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_ip_route.ref @@ -0,0 +1,77 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..a92272f6d0 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_ipv6_route.ref @@ -0,0 +1,40 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..c70b44e1c9 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,58 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt4", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1-2", + "neighbor-sysid": "0000.0000.0004", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-sw1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0001", + "hold-timer": 9, + "neighbor-priority": 100, + "state": "up" + }, + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 64, + "state": "up" + }, + { + "neighbor-sys-type": "level-2", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 64, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt2/zebra.conf b/tests/topotests/isis-lsp-bits-topo1/rt2/zebra.conf new file mode 100644 index 0000000000..234e10efa9 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt2/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt2 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 2.2.2.2/32 + ipv6 address 2001:db8:1000::2/128 +! +interface eth-sw1 + ip address 10.0.1.2/24 +! +interface eth-rt4 + ip address 10.0.2.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-lsp-bits-topo1/rt3/isisd.conf b/tests/topotests/isis-lsp-bits-topo1/rt3/isisd.conf new file mode 100644 index 0000000000..9ad97109b5 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt3/isisd.conf @@ -0,0 +1,35 @@ +hostname rt3 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt5 + ip router isis 2 + ipv6 router isis 2 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0003.00 + lsp-gen-interval 2 + topology ipv6-unicast +! +router isis 2 + net 49.0002.0000.0000.0003.00 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_ip_route.ref new file mode 100644 index 0000000000..55f0aedef5 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_ip_route.ref @@ -0,0 +1,97 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..5d6dfca76a --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_ipv6_route.ref @@ -0,0 +1,59 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..6950086b1e --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,51 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt5", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1-2", + "neighbor-sysid": "0000.0000.0005", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-sw1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0001", + "hold-timer": 9, + "neighbor-priority": 100, + "state": "up" + }, + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 64, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt3/zebra.conf b/tests/topotests/isis-lsp-bits-topo1/rt3/zebra.conf new file mode 100644 index 0000000000..9a0defd62b --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt3/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt3 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 3.3.3.3/32 + ipv6 address 2001:db8:1000::3/128 +! +interface eth-sw1 + ip address 10.0.1.3/24 +! +interface eth-rt5 + ip address 10.0.4.3/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-lsp-bits-topo1/rt4/isisd.conf b/tests/topotests/isis-lsp-bits-topo1/rt4/isisd.conf new file mode 100644 index 0000000000..e85412a71d --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt4/isisd.conf @@ -0,0 +1,42 @@ +hostname rt4 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 4 + ipv6 router isis 4 + isis passive +! +interface eth-rt2 + ip router isis 2 + ipv6 router isis 2 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt5 + ip router isis 4 + ipv6 router isis 4 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt6 + ip router isis 4 + ipv6 router isis 4 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 2 + net 49.0002.0000.0000.0004.00 + lsp-gen-interval 2 + topology ipv6-unicast +! +router isis 4 + net 49.0004.0000.0000.0004.00 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_ip_route.ref new file mode 100644 index 0000000000..2cf5c40635 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_ip_route.ref @@ -0,0 +1,94 @@ +{ + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.6.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.6.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..cde7287943 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_ipv6_route.ref @@ -0,0 +1,21 @@ +{ + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..233180ceb8 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,63 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1-2", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt5", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1-2", + "neighbor-sysid": "0000.0000.0005", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt6", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0006", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt4/zebra.conf b/tests/topotests/isis-lsp-bits-topo1/rt4/zebra.conf new file mode 100644 index 0000000000..adcf433249 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt4/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +! +hostname rt4 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 4.4.4.4/32 + ipv6 address 2001:db8:1000::4/128 +! +interface eth-rt2 + ip address 10.0.2.4/24 +! +interface eth-rt5 + ip address 10.0.6.4/24 +! +interface eth-rt6 + ip address 10.0.7.4/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-lsp-bits-topo1/rt5/isisd.conf b/tests/topotests/isis-lsp-bits-topo1/rt5/isisd.conf new file mode 100644 index 0000000000..2cab0c88fc --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt5/isisd.conf @@ -0,0 +1,42 @@ +hostname rt5 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 2 + ipv6 router isis 2 + isis passive +! +interface eth-rt3 + ip router isis 2 + ipv6 router isis 2 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt4 + ip router isis 4 + ipv6 router isis 4 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt6 + ip router isis 4 + ipv6 router isis 4 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 2 + net 49.0002.0000.0000.0005.00 + lsp-gen-interval 2 + topology ipv6-unicast +! +router isis 4 + net 49.0004.0000.0000.0005.00 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_ip_route.ref new file mode 100644 index 0000000000..fe34b03890 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_ip_route.ref @@ -0,0 +1,118 @@ +{ + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.6.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.6.4", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "table":254, + "nexthops":[ + { + "fib":true, + "ip":"10.0.6.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..7586c73852 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_ipv6_route.ref @@ -0,0 +1,40 @@ +{ + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..f939a6abff --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,63 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt3", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1-2", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt4", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1-2", + "neighbor-sysid": "0000.0000.0004", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt6", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0006", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt5/zebra.conf b/tests/topotests/isis-lsp-bits-topo1/rt5/zebra.conf new file mode 100644 index 0000000000..0f10ce921f --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt5/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +! +hostname rt5 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 5.5.5.5/32 + ipv6 address 2001:db8:1000::5/128 +! +interface eth-rt3 + ip address 10.0.4.5/24 +! +interface eth-rt4 + ip address 10.0.6.5/24 +! +interface eth-rt6 + ip address 10.0.8.5/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/isisd.conf b/tests/topotests/isis-lsp-bits-topo1/rt6/isisd.conf new file mode 100644 index 0000000000..249f945e0c --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt6/isisd.conf @@ -0,0 +1,32 @@ +hostname rt6 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 4 + ipv6 router isis 4 + isis passive +! +interface eth-rt4 + ip router isis 4 + ipv6 router isis 4 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt5 + ip router isis 4 + ipv6 router isis 4 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 4 + net 49.0004.0000.0000.0006.00 + is-type level-1 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_ip_route.ref new file mode 100644 index 0000000000..2840514e6e --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_ip_route.ref @@ -0,0 +1,107 @@ +{ + "0.0.0.0\/0":[ + { + "prefix":"0.0.0.0\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..278129f481 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_ipv6_route.ref @@ -0,0 +1,46 @@ +{ + "::\/0":[ + { + "prefix":"::\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..b4e8c23189 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,44 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt4", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1-2", + "neighbor-sysid": "0000.0000.0004", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt5", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1-2", + "neighbor-sysid": "0000.0000.0005", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/step2/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt6/step2/show_ip_route.ref new file mode 100644 index 0000000000..61efcb9e98 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step2/show_ip_route.ref @@ -0,0 +1,100 @@ +{ + "0.0.0.0\/0":[ + { + "prefix":"0.0.0.0\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/step2/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt6/step2/show_ipv6_route.ref new file mode 100644 index 0000000000..2e982e0c37 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step2/show_ipv6_route.ref @@ -0,0 +1,40 @@ +{ + "::\/0":[ + { + "prefix":"::\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/step3/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt6/step3/show_ip_route.ref new file mode 100644 index 0000000000..8fecf14687 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step3/show_ip_route.ref @@ -0,0 +1,80 @@ +{ + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/step3/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt6/step3/show_ipv6_route.ref new file mode 100644 index 0000000000..9b53a1d760 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step3/show_ipv6_route.ref @@ -0,0 +1,21 @@ +{ + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/step4/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt6/step4/show_ip_route.ref new file mode 100644 index 0000000000..2840514e6e --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step4/show_ip_route.ref @@ -0,0 +1,107 @@ +{ + "0.0.0.0\/0":[ + { + "prefix":"0.0.0.0\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/step4/show_ipv6_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt6/step4/show_ipv6_route.ref new file mode 100644 index 0000000000..278129f481 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step4/show_ipv6_route.ref @@ -0,0 +1,46 @@ +{ + "::\/0":[ + { + "prefix":"::\/0", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/zebra.conf b/tests/topotests/isis-lsp-bits-topo1/rt6/zebra.conf new file mode 100644 index 0000000000..6084010a93 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/rt6/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt6 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 6.6.6.6/32 + ipv6 address 2001:db8:1000::6/128 +! +interface eth-rt4 + ip address 10.0.7.6/24 +! +interface eth-rt5 + ip address 10.0.8.6/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-lsp-bits-topo1/test_isis_lsp_bits_topo1.py b/tests/topotests/isis-lsp-bits-topo1/test_isis_lsp_bits_topo1.py new file mode 100755 index 0000000000..95a0d87c33 --- /dev/null +++ b/tests/topotests/isis-lsp-bits-topo1/test_isis_lsp_bits_topo1.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python + +# +# test_isis_lsp_bits_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by Volta Networks +# +# 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_isis_lsp_bits_topo1.py: + + +---------+ + | | + | RT1 | + | 1.1.1.1 | + | L1 | + +---------+ + |eth-sw1 + | + | + | + +---------+ | +---------+ + | | | | | + | RT2 |eth-sw1 | eth-sw1| RT3 | + | 2.2.2.2 +----------+----------+ 3.3.3.3 | + | L1|L2 | 10.0.1.0/24 | L1|L2 | + +---------+ +---------+ + eth-rt4| eth-rt5| + | | + 10.0.2.0/24| |10.0.4.0/24 + | | + eth-rt2| eth-rt3| + +---------+ +---------+ + | | | | + | RT4 | 10.0.6.0/24 | RT5 | + | 4.4.4.4 +---------------------+ 5.5.5.5 | + | L1|L2 |eth-rt5 eth-rt4| L1|L2 | + +---------+ +---------+ + eth-rt6| |eth-rt6 + | | + 10.0.7.0/24| |10.0.8.0/24 + | +---------+ | + | | | | + | | RT6 | | + +----------+ 6.6.6.6 +-----------+ + eth-rt4| L1 |eth-rt5 + +---------+ +""" + +import os +import sys +import pytest +import json +import re +import tempfile +from time import sleep +from functools import partial + +# 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 + +# Global multi-dimensional dictionary containing all expected outputs +outputs = {} + +class TemplateTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['rt1'], nodeif="eth-sw1") + switch.add_link(tgen.gears['rt2'], nodeif="eth-sw1") + switch.add_link(tgen.gears['rt3'], nodeif="eth-sw1") + + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['rt2'], nodeif="eth-rt4") + switch.add_link(tgen.gears['rt4'], nodeif="eth-rt2") + + switch = tgen.add_switch('s4') + switch.add_link(tgen.gears['rt3'], nodeif="eth-rt5") + switch.add_link(tgen.gears['rt5'], nodeif="eth-rt3") + + switch = tgen.add_switch('s6') + switch.add_link(tgen.gears['rt4'], nodeif="eth-rt5") + switch.add_link(tgen.gears['rt5'], nodeif="eth-rt4") + + switch = tgen.add_switch('s7') + switch.add_link(tgen.gears['rt4'], nodeif="eth-rt6") + switch.add_link(tgen.gears['rt6'], nodeif="eth-rt4") + + switch = tgen.add_switch('s8') + switch.add_link(tgen.gears['rt5'], nodeif="eth-rt6") + switch.add_link(tgen.gears['rt6'], nodeif="eth-rt5") + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, + os.path.join(CWD, '{}/isisd.conf'.format(rname)) + ) + + tgen.start_router() + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + +def router_compare_json_output(rname, command, reference): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + +# +# Step 1 +# +# Test initial network convergence +# Attach-bit defaults to on, so expect default route pointing to L1|L2 router +# +def test_isis_adjacencies_step1(): + logger.info("Test (step 1): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output( + rname, + "show yang operational-data /frr-interface:lib isisd", + "step1/show_yang_interface_isis_adjacencies.ref", + ) + +def test_rib_ipv4_step1(): + logger.info("Test (step 1): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output( + rname, "show ip route isis json", "step1/show_ip_route.ref" + ) + +def test_rib_ipv6_step1(): + logger.info("Test (step 1): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output( + rname, "show ipv6 route isis json", "step1/show_ipv6_route.ref" + ) + +# +# Step 2 +# +# Action(s): +# -Disable sending Attach bit on RT2 and RT4 +# +# Expected changes: +# -RT1 should remove the default route pointing to RT2 +# -RT6 should remove the default route pointing to RT4 +# +def test_rib_ipv4_step2(): + logger.info("Test (step 2): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Disabling setting the attached-bit on RT2 and RT4') + tgen.net['rt2'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no attached-bit send"') + tgen.net['rt4'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no attached-bit send"') + + for rname in ['rt1', 'rt6']: + router_compare_json_output( + rname, "show ip route isis json", "step2/show_ip_route.ref" + ) + +def test_rib_ipv6_step2(): + logger.info("Test (step 2): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt6']: + router_compare_json_output( + rname, "show ipv6 route isis json", "step2/show_ipv6_route.ref" + ) + +# +# Step 3 +# +# Action(s): +# -restore attach-bit, enable sending attach-bit +# -disble processing a LSP with attach bit set +# +# Expected changes: +# -RT1 and RT6 should not install a default route +# +def test_rib_ipv4_step3(): + logger.info("Test (step 3): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Enable setting the attached-bit on RT2 and RT4') + tgen.net['rt2'].cmd('vtysh -c "conf t" -c "router isis 1" -c "attached-bit send"') + tgen.net['rt4'].cmd('vtysh -c "conf t" -c "router isis 1" -c "attached-bit send"') + + logger.info('Disable processing received attached-bit in LSP on RT1 and RT6') + tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "attached-bit receive ignore"') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "router isis 1" -c "attached-bit receive ignore"') + + for rname in ['rt1', 'rt6']: + router_compare_json_output( + rname, "show ip route isis json", "step3/show_ip_route.ref" + ) + +def test_rib_ipv6_step3(): + logger.info("Test (step 3): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt6']: + router_compare_json_output( + rname, "show ipv6 route isis json", "step3/show_ipv6_route.ref" + ) + +# +# Step 4 +# +# Action(s): +# -restore back to default attach-bit config +# +# Expected changes: +# -RT1 and RT6 should add default route +# -no changes on other routers +# +def test_rib_ipv4_step4(): + logger.info("Test (step 4): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('restore default processing on received attached-bit in LSP on RT1 and RT6') + tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no attached-bit receive ignore"') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no attached-bit receive ignore"') + + for rname in ['rt1', 'rt6']: + router_compare_json_output( + rname, "show ip route isis json", "step4/show_ip_route.ref") + +def test_rib_ipv6_step4(): + logger.info("Test (step 4): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt6']: + router_compare_json_output( + rname, "show ipv6 route isis json", "step4/show_ipv6_route.ref") + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis-rlfa-topo1/__init__.py b/tests/topotests/isis-rlfa-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/__init__.py diff --git a/tests/topotests/isis-rlfa-topo1/rt1/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt1/isisd.conf new file mode 100644 index 0000000000..a80f30dc7b --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/isisd.conf @@ -0,0 +1,39 @@ +password 1 +hostname rt1 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis fast-reroute lfa + isis fast-reroute remote-lfa tunnel mpls-ldp +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis fast-reroute lfa + isis fast-reroute remote-lfa tunnel mpls-ldp +! +ip prefix-list PLIST seq 5 permit 10.0.255.8/32 +! +router isis 1 + net 49.0000.0000.0000.0001.00 + is-type level-1-2 + lsp-gen-interval 2 + topology ipv6-unicast + fast-reroute remote-lfa prefix-list PLIST +! diff --git a/tests/topotests/isis-rlfa-topo1/rt1/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt1/ldpd.conf new file mode 100644 index 0000000000..f60fdb9742 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/ldpd.conf @@ -0,0 +1,30 @@ +log file ldpd.log +! +hostname rt1 +! +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp zebra +! +mpls ldp + router-id 10.0.255.1 + dual-stack transport-connection prefer ipv4 + ! + address-family ipv4 + label local allocate host-routes + discovery targeted-hello accept + discovery transport-address 10.0.255.1 + ! + interface eth-rt2 + interface eth-rt3 + ! + ! + address-family ipv6 + label local allocate host-routes + discovery transport-address 2001:db8::1 + ! + interface eth-rt2 + interface eth-rt3 + ! + ! +! diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ip_route.ref b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ip_route.ref new file mode 100644 index 0000000000..680b31eb8d --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ip_route.ref @@ -0,0 +1,235 @@ +{ + "10.0.255.2\/32":[ + { + "prefix":"10.0.255.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.255.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, + "onLink":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "ip":"10.0.255.3", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, + "onLink":true, + "labels":"*" + } + ] + } + ], + "10.0.255.3\/32":[ + { + "prefix":"10.0.255.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.255.3", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, + "onLink":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "ip":"10.0.255.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, + "onLink":true, + "labels":"*" + } + ] + } + ], + "10.0.255.4\/32":[ + { + "prefix":"10.0.255.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.255.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, + "onLink":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "ip":"10.0.255.3", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, + "onLink":true, + "labels":"*" + } + ] + } + ], + "10.0.255.5\/32":[ + { + "prefix":"10.0.255.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.255.3", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, + "onLink":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "ip":"10.0.255.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, + "onLink":true, + "labels":"*" + } + ] + } + ], + "10.0.255.6\/32":[ + { + "prefix":"10.0.255.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.255.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, + "onLink":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "ip":"10.0.255.3", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, + "onLink":true, + "labels":"*" + } + ] + } + ], + "10.0.255.7\/32":[ + { + "prefix":"10.0.255.7\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.255.3", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, + "onLink":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "ip":"10.0.255.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, + "onLink":true, + "labels":"*" + } + ] + } + ], + "10.0.255.8\/32":[ + { + "prefix":"10.0.255.8\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":50, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.255.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, + "onLink":true + }, + { + "fib":true, + "ip":"10.0.255.3", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, + "onLink":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..c487d2740d --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ipv6_route.ref @@ -0,0 +1,207 @@ +{ + "2001:db8::2\/128":[ + { + "prefix":"2001:db8::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true, + "labels":"*" + } + ] + } + ], + "2001:db8::3\/128":[ + { + "prefix":"2001:db8::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true, + "labels":"*" + } + ] + } + ], + "2001:db8::4\/128":[ + { + "prefix":"2001:db8::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true, + "labels":"*" + } + ] + } + ], + "2001:db8::5\/128":[ + { + "prefix":"2001:db8::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true, + "labels":"*" + } + ] + } + ], + "2001:db8::6\/128":[ + { + "prefix":"2001:db8::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true, + "labels":"*" + } + ] + } + ], + "2001:db8::7\/128":[ + { + "prefix":"2001:db8::7\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true, + "backupIndex":[ + 0 + ] + } + ], + "backupNexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true, + "labels":"*" + } + ] + } + ], + "2001:db8::8\/128":[ + { + "prefix":"2001:db8::8\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":50, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..3fe2b798a0 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,44 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1-2", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt3", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1-2", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ip_route.ref.diff new file mode 100644 index 0000000000..ef5707f14a --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ip_route.ref.diff @@ -0,0 +1,68 @@ +--- a/rt1/step9/show_ip_route.ref ++++ b/rt1/step10/show_ip_route.ref +@@ -15,7 +15,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.3", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -70,7 +83,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.3", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -125,7 +151,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.3", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ipv6_route.ref.diff new file mode 100644 index 0000000000..acd2ce003a --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ipv6_route.ref.diff @@ -0,0 +1,62 @@ +--- a/rt1/step9/show_ipv6_route.ref ++++ b/rt1/step10/show_ipv6_route.ref +@@ -13,7 +13,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -62,7 +73,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -111,7 +133,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "labels":"*" + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ip_route.ref.diff new file mode 100644 index 0000000000..f7f31ac021 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ip_route.ref.diff @@ -0,0 +1,134 @@ +--- a/rt1/step1/show_ip_route.ref ++++ b/rt1/step2/show_ip_route.ref +@@ -15,20 +15,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.3", +- "afi":"ipv4", +- "interfaceName":"eth-rt3", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } +@@ -49,20 +36,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.2", +- "afi":"ipv4", +- "interfaceName":"eth-rt2", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } +@@ -83,20 +57,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.3", +- "afi":"ipv4", +- "interfaceName":"eth-rt3", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } +@@ -117,20 +78,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.2", +- "afi":"ipv4", +- "interfaceName":"eth-rt2", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } +@@ -151,20 +99,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.3", +- "afi":"ipv4", +- "interfaceName":"eth-rt3", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } +@@ -185,20 +120,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.2", +- "afi":"ipv4", +- "interfaceName":"eth-rt2", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ipv6_route.ref.diff new file mode 100644 index 0000000000..e980031ad7 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ipv6_route.ref.diff @@ -0,0 +1,122 @@ +--- a/rt1/step1/show_ipv6_route.ref ++++ b/rt1/step2/show_ipv6_route.ref +@@ -13,18 +13,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt3", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } +@@ -43,18 +32,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt2", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } +@@ -73,18 +51,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt3", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } +@@ -103,18 +70,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt2", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } +@@ -133,18 +89,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt3", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } +@@ -163,18 +108,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt2", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ip_route.ref.diff new file mode 100644 index 0000000000..f3ed764f0b --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ip_route.ref.diff @@ -0,0 +1,134 @@ +--- a/rt1/step2/show_ip_route.ref ++++ b/rt1/step3/show_ip_route.ref +@@ -15,7 +15,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.3", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -36,7 +49,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.2", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -57,7 +83,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.3", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -78,7 +117,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.2", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -99,7 +151,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.3", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -120,7 +185,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.2", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ipv6_route.ref.diff new file mode 100644 index 0000000000..57b0b1de1a --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ipv6_route.ref.diff @@ -0,0 +1,122 @@ +--- a/rt1/step2/show_ipv6_route.ref ++++ b/rt1/step3/show_ipv6_route.ref +@@ -13,7 +13,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -32,7 +43,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -51,7 +73,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -70,7 +103,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -89,7 +133,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -108,7 +163,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "labels":"*" + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ip_route.ref.diff new file mode 100644 index 0000000000..107a0ba2f7 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ip_route.ref.diff @@ -0,0 +1,68 @@ +--- a/rt1/step3/show_ip_route.ref ++++ b/rt1/step4/show_ip_route.ref +@@ -15,20 +15,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.3", +- "afi":"ipv4", +- "interfaceName":"eth-rt3", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } +@@ -83,20 +70,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.3", +- "afi":"ipv4", +- "interfaceName":"eth-rt3", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } +@@ -151,20 +125,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.3", +- "afi":"ipv4", +- "interfaceName":"eth-rt3", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ipv6_route.ref.diff new file mode 100644 index 0000000000..9cf24082e1 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ipv6_route.ref.diff @@ -0,0 +1,62 @@ +--- a/rt1/step3/show_ipv6_route.ref ++++ b/rt1/step4/show_ipv6_route.ref +@@ -13,18 +13,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt3", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } +@@ -73,18 +62,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt3", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } +@@ -133,18 +111,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt3", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ip_route.ref.diff new file mode 100644 index 0000000000..09469501f5 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ip_route.ref.diff @@ -0,0 +1,68 @@ +--- a/rt1/step4/show_ip_route.ref ++++ b/rt1/step5/show_ip_route.ref +@@ -36,20 +36,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.2", +- "afi":"ipv4", +- "interfaceName":"eth-rt2", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } +@@ -91,20 +78,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.2", +- "afi":"ipv4", +- "interfaceName":"eth-rt2", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } +@@ -146,20 +120,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.2", +- "afi":"ipv4", +- "interfaceName":"eth-rt2", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ipv6_route.ref.diff new file mode 100644 index 0000000000..70fb1a65c7 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ipv6_route.ref.diff @@ -0,0 +1,62 @@ +--- a/rt1/step4/show_ipv6_route.ref ++++ b/rt1/step5/show_ipv6_route.ref +@@ -32,18 +32,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt2", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } +@@ -81,18 +70,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt2", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } +@@ -130,18 +108,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt2", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ip_route.ref.diff new file mode 100644 index 0000000000..4e4a5692a4 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ip_route.ref.diff @@ -0,0 +1,134 @@ +--- a/rt1/step5/show_ip_route.ref ++++ b/rt1/step6/show_ip_route.ref +@@ -15,7 +15,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.3", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -36,7 +49,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.2", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -57,7 +83,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.3", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -78,7 +117,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.2", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -99,7 +151,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.3", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } +@@ -120,7 +185,20 @@ + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true, +- "onLink":true ++ "onLink":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "ip":"10.0.255.2", ++ "afi":"ipv4", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "onLink":true, ++ "labels":"*" + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ipv6_route.ref.diff new file mode 100644 index 0000000000..c9ebb1e3f5 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ipv6_route.ref.diff @@ -0,0 +1,122 @@ +--- a/rt1/step5/show_ipv6_route.ref ++++ b/rt1/step6/show_ipv6_route.ref +@@ -13,7 +13,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -32,7 +43,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -51,7 +73,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -70,7 +103,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -89,7 +133,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt3", ++ "active":true, ++ "labels":"*" + } + ] + } +@@ -108,7 +163,18 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", +- "active":true ++ "active":true, ++ "backupIndex":[ ++ 0 ++ ] ++ } ++ ], ++ "backupNexthops":[ ++ { ++ "afi":"ipv6", ++ "interfaceName":"eth-rt2", ++ "active":true, ++ "labels":"*" + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ip_route.ref.diff new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ip_route.ref.diff diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ipv6_route.ref.diff new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ipv6_route.ref.diff diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ip_route.ref.diff new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ip_route.ref.diff diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ipv6_route.ref.diff new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ipv6_route.ref.diff diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ip_route.ref.diff new file mode 100644 index 0000000000..33eb6577bd --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ip_route.ref.diff @@ -0,0 +1,68 @@ +--- a/rt1/step8/show_ip_route.ref ++++ b/rt1/step9/show_ip_route.ref +@@ -15,20 +15,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.3", +- "afi":"ipv4", +- "interfaceName":"eth-rt3", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } +@@ -83,20 +70,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.3", +- "afi":"ipv4", +- "interfaceName":"eth-rt3", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } +@@ -151,20 +125,7 @@ + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true, +- "onLink":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "ip":"10.0.255.3", +- "afi":"ipv4", +- "interfaceName":"eth-rt3", +- "active":true, +- "onLink":true, +- "labels":"*" ++ "onLink":true + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ipv6_route.ref.diff new file mode 100644 index 0000000000..7aaca3354e --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ipv6_route.ref.diff @@ -0,0 +1,62 @@ +--- a/rt1/step8/show_ipv6_route.ref ++++ b/rt1/step9/show_ipv6_route.ref +@@ -13,18 +13,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt3", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } +@@ -73,18 +62,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt3", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } +@@ -133,18 +111,7 @@ + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", +- "active":true, +- "backupIndex":[ +- 0 +- ] +- } +- ], +- "backupNexthops":[ +- { +- "afi":"ipv6", +- "interfaceName":"eth-rt3", +- "active":true, +- "labels":"*" ++ "active":true + } + ] + } diff --git a/tests/topotests/isis-rlfa-topo1/rt1/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt1/zebra.conf new file mode 100644 index 0000000000..741fc2d02b --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt1/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt1 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 10.0.255.1/32 + ipv6 address 2001:db8::1/128 +! +interface eth-rt2 + ip address 10.0.255.1/32 +! +interface eth-rt3 + ip address 10.0.255.1/32 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-rlfa-topo1/rt2/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt2/isisd.conf new file mode 100644 index 0000000000..7b4c6c50b9 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt2/isisd.conf @@ -0,0 +1,32 @@ +password 1 +hostname rt2 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + net 49.0000.0000.0000.0002.00 + is-type level-1-2 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-rlfa-topo1/rt2/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt2/ldpd.conf new file mode 100644 index 0000000000..0a815ef004 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt2/ldpd.conf @@ -0,0 +1,30 @@ +log file ldpd.log +! +hostname rt2 +! +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp zebra +! +mpls ldp + router-id 10.0.255.2 + dual-stack transport-connection prefer ipv4 + ! + address-family ipv4 + label local allocate host-routes + discovery targeted-hello accept + discovery transport-address 10.0.255.2 + ! + interface eth-rt1 + interface eth-rt4 + ! + ! + address-family ipv6 + label local allocate host-routes + discovery transport-address 2001:db8::2 + ! + interface eth-rt1 + interface eth-rt4 + ! + ! +! diff --git a/tests/topotests/isis-rlfa-topo1/rt2/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt2/zebra.conf new file mode 100644 index 0000000000..657c69bf28 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt2/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt2 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 10.0.255.2/32 + ipv6 address 2001:db8::2/128 +! +interface eth-rt1 + ip address 10.0.255.2/32 +! +interface eth-rt4 + ip address 10.0.255.2/32 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-rlfa-topo1/rt3/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt3/isisd.conf new file mode 100644 index 0000000000..17d58a9d15 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt3/isisd.conf @@ -0,0 +1,32 @@ +password 1 +hostname rt3 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + net 49.0000.0000.0000.0003.00 + is-type level-1-2 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-rlfa-topo1/rt3/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt3/ldpd.conf new file mode 100644 index 0000000000..40f1f5587a --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt3/ldpd.conf @@ -0,0 +1,30 @@ +log file ldpd.log +! +hostname rt3 +! +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp zebra +! +mpls ldp + router-id 10.0.255.3 + dual-stack transport-connection prefer ipv4 + ! + address-family ipv4 + label local allocate host-routes + discovery targeted-hello accept + discovery transport-address 10.0.255.3 + ! + interface eth-rt1 + interface eth-rt5 + ! + ! + address-family ipv6 + label local allocate host-routes + discovery transport-address 2001:db8::3 + ! + interface eth-rt1 + interface eth-rt5 + ! + ! +! diff --git a/tests/topotests/isis-rlfa-topo1/rt3/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt3/zebra.conf new file mode 100644 index 0000000000..86f5d2871a --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt3/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt3 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 10.0.255.3/32 + ipv6 address 2001:db8::3/128 +! +interface eth-rt1 + ip address 10.0.255.3/32 +! +interface eth-rt5 + ip address 10.0.255.3/32 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-rlfa-topo1/rt4/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt4/isisd.conf new file mode 100644 index 0000000000..1519fd4c16 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt4/isisd.conf @@ -0,0 +1,32 @@ +password 1 +hostname rt4 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + net 49.0000.0000.0000.0004.00 + is-type level-1-2 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-rlfa-topo1/rt4/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt4/ldpd.conf new file mode 100644 index 0000000000..569ecf733e --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt4/ldpd.conf @@ -0,0 +1,30 @@ +log file ldpd.log +! +hostname rt4 +! +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp zebra +! +mpls ldp + router-id 10.0.255.4 + dual-stack transport-connection prefer ipv4 + ! + address-family ipv4 + label local allocate host-routes + discovery targeted-hello accept + discovery transport-address 10.0.255.4 + ! + interface eth-rt2 + interface eth-rt6 + ! + ! + address-family ipv6 + label local allocate host-routes + discovery transport-address 2001:db8::4 + ! + interface eth-rt2 + interface eth-rt6 + ! + ! +! diff --git a/tests/topotests/isis-rlfa-topo1/rt4/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt4/zebra.conf new file mode 100644 index 0000000000..1dd09bf83b --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt4/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt4 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 10.0.255.4/32 + ipv6 address 2001:db8::4/128 +! +interface eth-rt2 + ip address 10.0.255.4/32 +! +interface eth-rt6 + ip address 10.0.255.4/32 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-rlfa-topo1/rt5/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt5/isisd.conf new file mode 100644 index 0000000000..caf7477073 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt5/isisd.conf @@ -0,0 +1,32 @@ +password 1 +hostname rt5 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface eth-rt7 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + net 49.0000.0000.0000.0005.00 + is-type level-1-2 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-rlfa-topo1/rt5/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt5/ldpd.conf new file mode 100644 index 0000000000..519c3d3628 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt5/ldpd.conf @@ -0,0 +1,30 @@ +log file ldpd.log +! +hostname rt5 +! +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp zebra +! +mpls ldp + router-id 10.0.255.5 + dual-stack transport-connection prefer ipv4 + ! + address-family ipv4 + label local allocate host-routes + discovery targeted-hello accept + discovery transport-address 10.0.255.5 + ! + interface eth-rt3 + interface eth-rt7 + ! + ! + address-family ipv6 + label local allocate host-routes + discovery transport-address 2001:db8::5 + ! + interface eth-rt3 + interface eth-rt7 + ! + ! +! diff --git a/tests/topotests/isis-rlfa-topo1/rt5/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt5/zebra.conf new file mode 100644 index 0000000000..7117a2a2e3 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt5/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt5 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 10.0.255.5/32 + ipv6 address 2001:db8::5/128 +! +interface eth-rt3 + ip address 10.0.255.5/32 +! +interface eth-rt7 + ip address 10.0.255.5/32 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-rlfa-topo1/rt6/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt6/isisd.conf new file mode 100644 index 0000000000..cdf6267236 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt6/isisd.conf @@ -0,0 +1,32 @@ +password 1 +hostname rt6 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface eth-rt8 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + net 49.0000.0000.0000.0006.00 + is-type level-1-2 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-rlfa-topo1/rt6/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt6/ldpd.conf new file mode 100644 index 0000000000..a5b7062bec --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt6/ldpd.conf @@ -0,0 +1,30 @@ +log file ldpd.log +! +hostname rt6 +! +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp zebra +! +mpls ldp + router-id 10.0.255.6 + dual-stack transport-connection prefer ipv4 + ! + address-family ipv4 + label local allocate host-routes + discovery targeted-hello accept + discovery transport-address 10.0.255.6 + ! + interface eth-rt4 + interface eth-rt8 + ! + ! + address-family ipv6 + label local allocate host-routes + discovery transport-address 2001:db8::6 + ! + interface eth-rt4 + interface eth-rt8 + ! + ! +! diff --git a/tests/topotests/isis-rlfa-topo1/rt6/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt6/zebra.conf new file mode 100644 index 0000000000..c6344870b7 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt6/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt6 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 10.0.255.6/32 + ipv6 address 2001:db8::6/128 +! +interface eth-rt4 + ip address 10.0.255.6/32 +! +interface eth-rt8 + ip address 10.0.255.6/32 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-rlfa-topo1/rt7/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt7/isisd.conf new file mode 100644 index 0000000000..8ab8fcb232 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt7/isisd.conf @@ -0,0 +1,32 @@ +password 1 +hostname rt7 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface eth-rt8 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + net 49.0000.0000.0000.0007.00 + is-type level-1-2 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-rlfa-topo1/rt7/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt7/ldpd.conf new file mode 100644 index 0000000000..26d428c4c6 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt7/ldpd.conf @@ -0,0 +1,30 @@ +log file ldpd.log +! +hostname rt7 +! +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp zebra +! +mpls ldp + router-id 10.0.255.7 + dual-stack transport-connection prefer ipv4 + ! + address-family ipv4 + label local allocate host-routes + discovery targeted-hello accept + discovery transport-address 10.0.255.7 + ! + interface eth-rt5 + interface eth-rt8 + ! + ! + address-family ipv6 + label local allocate host-routes + discovery transport-address 2001:db8::7 + ! + interface eth-rt5 + interface eth-rt8 + ! + ! +! diff --git a/tests/topotests/isis-rlfa-topo1/rt7/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt7/zebra.conf new file mode 100644 index 0000000000..4c5e0f1126 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt7/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt7 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 10.0.255.7/32 + ipv6 address 2001:db8::7/128 +! +interface eth-rt5 + ip address 10.0.255.7/32 +! +interface eth-rt8 + ip address 10.0.255.7/32 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-rlfa-topo1/rt8/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt8/isisd.conf new file mode 100644 index 0000000000..abdc6a53a5 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt8/isisd.conf @@ -0,0 +1,32 @@ +password 1 +hostname rt8 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface eth-rt7 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + net 49.0000.0000.0000.0008.00 + is-type level-1-2 + lsp-gen-interval 2 + topology ipv6-unicast +! diff --git a/tests/topotests/isis-rlfa-topo1/rt8/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt8/ldpd.conf new file mode 100644 index 0000000000..1629f82de1 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt8/ldpd.conf @@ -0,0 +1,30 @@ +log file ldpd.log +! +hostname rt8 +! +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp zebra +! +mpls ldp + router-id 10.0.255.8 + dual-stack transport-connection prefer ipv4 + ! + address-family ipv4 + label local allocate host-routes + discovery targeted-hello accept + discovery transport-address 10.0.255.8 + ! + interface eth-rt6 + interface eth-rt7 + ! + ! + address-family ipv6 + label local allocate host-routes + discovery transport-address 2001:db8::8 + ! + interface eth-rt6 + interface eth-rt7 + ! + ! +! diff --git a/tests/topotests/isis-rlfa-topo1/rt8/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt8/zebra.conf new file mode 100644 index 0000000000..f3f10f649a --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/rt8/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt8 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 10.0.255.8/32 + ipv6 address 2001:db8::8/128 +! +interface eth-rt6 + ip address 10.0.255.8/32 +! +interface eth-rt7 + ip address 10.0.255.8/32 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-rlfa-topo1/test_isis_rlfa_topo1.py b/tests/topotests/isis-rlfa-topo1/test_isis_rlfa_topo1.py new file mode 100755 index 0000000000..bb43e6b114 --- /dev/null +++ b/tests/topotests/isis-rlfa-topo1/test_isis_rlfa_topo1.py @@ -0,0 +1,662 @@ +#!/usr/bin/env python + +# +# test_isis_rlfa_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# 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_isis_rlfa_topo1.py: + + +---------+ +---------+ + | | | | + | RT1 | | RT2 | + | +---------------------+ | + | | | | + +---+-----+ +------+--+ + | | + | | + | | + +---+-----+ +------+--+ + | | | | + | RT3 | | RT4 | + | | | | + | | | | + +---+-----+ +------+--+ + | | + | | + | | + +---+-----+ +------+--+ + | | | | + | RT5 | | RT6 | + | | | | + | | | | + +---+-----+ +------+--+ + | | + | | + | | + +---+-----+ +------+--+ + | | | | + | RT7 | | RT8 | + | +---------------------+ | + | | | | + +---------+ +---------+ +""" + +import os +import sys +import pytest +import json +import re +import tempfile +from time import sleep +from functools import partial + +# 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 + +# Global multi-dimensional dictionary containing all expected outputs +outputs = {} + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7", "rt8"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1") + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1") + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2") + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3") + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4") + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt7") + switch.add_link(tgen.gears["rt7"], nodeif="eth-rt5") + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt8") + switch.add_link(tgen.gears["rt8"], nodeif="eth-rt6") + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["rt7"], nodeif="eth-rt8") + switch.add_link(tgen.gears["rt8"], nodeif="eth-rt7") + + # + # Populate multi-dimensional dictionary containing all expected outputs + # + files = [ + "show_ip_route.ref", + "show_ipv6_route.ref", + "show_yang_interface_isis_adjacencies.ref", + ] + for rname in ["rt1"]: + outputs[rname] = {} + for step in range(1, 10 + 1): + outputs[rname][step] = {} + for file in files: + if step == 1: + # Get snapshots relative to the expected initial network convergence + filename = "{}/{}/step{}/{}".format(CWD, rname, step, file) + outputs[rname][step][file] = open(filename).read() + else: + if file == "show_yang_interface_isis_adjacencies.ref": + continue + + # Get diff relative to the previous step + filename = "{}/{}/step{}/{}.diff".format(CWD, rname, step, file) + + # Create temporary files in order to apply the diff + f_in = tempfile.NamedTemporaryFile() + f_in.write(outputs[rname][step - 1][file]) + f_in.flush() + f_out = tempfile.NamedTemporaryFile() + os.system( + "patch -s -o %s %s %s" % (f_out.name, f_in.name, filename) + ) + + # Store the updated snapshot and remove the temporary files + outputs[rname][step][file] = open(f_out.name).read() + f_in.close() + f_out.close() + +@pytest.mark.isis +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + 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_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def router_compare_json_output(rname, command, reference): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + expected = json.loads(reference) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +# +# Step 1 +# +# Test initial network convergence +# +def test_isis_adjacencies_step1(): + logger.info("Test (step 1): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, + "show yang operational-data /frr-interface:lib isisd", + outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"], + ) + + +def test_rib_ipv4_step1(): + logger.info("Test (step 1): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][1]["show_ip_route.ref"] + ) + + +def test_rib_ipv6_step1(): + logger.info("Test (step 1): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][1]["show_ipv6_route.ref"] + ) + + +# +# Step 2 +# +# Action(s): +# -Configure rt8 (rt1's PQ router) to not accept targeted hello messages +# +# Expected changes: +# -All rt1 backup routes should be uninstalled +# +def test_rib_ipv4_step2(): + logger.info("Test (step 2): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring rt8 to not accept targeted hello messages") + tgen.net["rt8"].cmd( + 'vtysh -c "conf t" -c "mpls ldp" -c "address-family ipv4" -c "no discovery targeted-hello accept"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][2]["show_ip_route.ref"] + ) + + +def test_rib_ipv6_step2(): + logger.info("Test (step 2): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][2]["show_ipv6_route.ref"] + ) + + +# +# Step 3 +# +# Action(s): +# -Configure rt8 (rt1's PQ router) to accept targeted hello messages +# +# Expected changes: +# -All rt1 previously uninstalled backup routes should be reinstalled +# +def test_rib_ipv4_step3(): + logger.info("Test (step 3): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring rt8 to accept targeted hello messages") + tgen.net["rt8"].cmd( + 'vtysh -c "conf t" -c "mpls ldp" -c "address-family ipv4" -c "discovery targeted-hello accept"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][3]["show_ip_route.ref"] + ) + + +def test_rib_ipv6_step3(): + logger.info("Test (step 3): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][3]["show_ipv6_route.ref"] + ) + + +# +# Step 4 +# +# Action(s): +# -Disable RLFA on rt1's eth-rt2 interface +# +# Expected changes: +# -All non-ECMP routes whose primary nexthop is eth-rt2 should lose their backup nexthops +# +def test_rib_ipv4_step4(): + logger.info("Test (step 4): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Disabling RLFA on rt1's eth-rt2 interface") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute remote-lfa tunnel mpls-ldp"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][4]["show_ip_route.ref"] + ) + + +def test_rib_ipv6_step4(): + logger.info("Test (step 4): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][4]["show_ipv6_route.ref"] + ) + + +# +# Step 5 +# +# Action(s): +# -Disable RLFA on rt1's eth-rt3 interface +# +# Expected changes: +# -All non-ECMP routes whose primary nexthop is eth-rt3 should lose their backup nexthops +# +def test_rib_ipv4_step5(): + logger.info("Test (step 5): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Disabling RLFA on rt1's eth-rt3 interface") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt3" -c "no isis fast-reroute remote-lfa tunnel mpls-ldp"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][5]["show_ip_route.ref"] + ) + + +def test_rib_ipv6_step5(): + logger.info("Test (step 5): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][5]["show_ipv6_route.ref"] + ) + + +# +# Step 6 +# +# Action(s): +# -Re-enable RLFA on rt1's eth-rt2 and eth-rt3 interfaces +# +# Expected changes: +# -Revert changes from the previous two steps (reinstall all backup routes) +# +def test_rib_ipv4_step6(): + logger.info("Test (step 6): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Re-enabling RLFA on rt1's eth-rt2 and eth-rt3 interfaces") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa tunnel mpls-ldp"' + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt3" -c "isis fast-reroute remote-lfa tunnel mpls-ldp"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][6]["show_ip_route.ref"] + ) + + +def test_rib_ipv6_step6(): + logger.info("Test (step 6): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][6]["show_ipv6_route.ref"] + ) + + +# +# Step 7 +# +# Action(s): +# -Configure a PQ node prefix-list filter +# +# Expected changes: +# -All backup routes should be uninstalled +# +def test_rib_ipv4_step7(): + logger.info("Test (step 7): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring a PQ node prefix-list filter") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute remote-lfa prefix-list PLIST"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][7]["show_ip_route.ref"] + ) + + +def test_rib_ipv6_step7(): + logger.info("Test (step 7): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][7]["show_ipv6_route.ref"] + ) + + +# +# Step 8 +# +# Action(s): +# -Configure a prefix-list allowing rt8 as a PQ node +# +# Expected changes: +# -All backup routes should be installed again +# +def test_rib_ipv4_step8(): + logger.info("Test (step 8): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring a prefix-list allowing rt8 as a PQ node") + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "ip prefix-list PLIST seq 5 permit 10.0.255.8/32"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][8]["show_ip_route.ref"] + ) + + +def test_rib_ipv6_step8(): + logger.info("Test (step 8): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][8]["show_ipv6_route.ref"] + ) + + +# +# Step 9 +# +# Action(s): +# -Change the maximum metric up to the PQ node to 30 on the eth-rt2 interface +# +# Expected changes: +# -All non-ECMP routes whose primary nexthop is eth-rt2 should lose their backup nexthops +# +def test_rib_ipv4_step9(): + logger.info("Test (step 9): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Changing the maximum metric up to the PQ node to 30 on the eth-rt2 interface" + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa maximum-metric 30"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][9]["show_ip_route.ref"] + ) + + +def test_rib_ipv6_step9(): + logger.info("Test (step 9): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][9]["show_ipv6_route.ref"] + ) + + +# +# Step 10 +# +# Action(s): +# -Change the maximum metric up to the PQ node to 40 on the eth-rt2 interface +# +# Expected changes: +# -All non-ECMP routes whose primary nexthop is eth-rt2 should recover their backup nexthops +# +def test_rib_ipv4_step10(): + logger.info("Test (step 10): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Changing the maximum metric up to the PQ node to 40 on the eth-rt2 interface" + ) + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa maximum-metric 40"' + ) + + for rname in ["rt1"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][10]["show_ip_route.ref"] + ) + + +def test_rib_ipv6_step10(): + logger.info("Test (step 10): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1"]: + router_compare_json_output( + rname, + "show ipv6 route isis json", + outputs[rname][10]["show_ipv6_route.ref"], + ) + + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis-sr-te-topo1/dst/zebra.conf b/tests/topotests/isis-sr-te-topo1/dst/zebra.conf new file mode 100644 index 0000000000..e873ac8a5c --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/dst/zebra.conf @@ -0,0 +1,19 @@ +log file zebra.log +! +hostname dst +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 9.9.9.2/32 + ipv6 address 2001:db8:1066::2/128 +! +interface eth-rt6 + ip address 10.0.11.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt1/bgpd.conf b/tests/topotests/isis-sr-te-topo1/rt1/bgpd.conf new file mode 100644 index 0000000000..efc03701b5 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/bgpd.conf @@ -0,0 +1,16 @@ +log file bgpd.log +! +router bgp 1 + bgp router-id 1.1.1.1 + neighbor 6.6.6.6 remote-as 1 + neighbor 6.6.6.6 update-source lo + ! + address-family ipv4 unicast + redistribute static + neighbor 6.6.6.6 next-hop-self + neighbor 6.6.6.6 route-map SET_SR_POLICY in + exit-address-family +! +route-map SET_SR_POLICY permit 10 + set sr-te color 1 +! diff --git a/tests/topotests/isis-sr-te-topo1/rt1/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt1/isisd.conf new file mode 100644 index 0000000000..70ae1b07f5 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/isisd.conf @@ -0,0 +1,30 @@ +password 1 +hostname rt1 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0001.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 10 + segment-routing prefix 2001:db8:1000::1/128 index 11 +! diff --git a/tests/topotests/isis-sr-te-topo1/rt1/pathd.conf b/tests/topotests/isis-sr-te-topo1/rt1/pathd.conf new file mode 100644 index 0000000000..911971496e --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/pathd.conf @@ -0,0 +1,21 @@ +log file pathd.log +! +hostname rt1 +! +segment-routing + traffic-eng + segment-list default + index 10 mpls label 16050 + index 20 mpls label 16060 + ! + segment-list test + index 10 mpls label 16020 + index 20 mpls label 16040 + index 30 mpls label 16060 + ! + policy color 1 endpoint 6.6.6.6 + name default + binding-sid 1111 + ! + ! +!
\ No newline at end of file diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref new file mode 100644 index 0000000000..d4b27d157d --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref @@ -0,0 +1,91 @@ +{ + "1111":{ + "inLabel":1111, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":16050, + "outLabelStack":[ + 16050, + 16060 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "distance":150, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "distance":150, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "distance":150, + "installed":true, + "nexthop":"10.0.1.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref new file mode 100644 index 0000000000..5fe58d0824 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref @@ -0,0 +1,74 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "distance":150, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "distance":150, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "distance":150, + "installed":true, + "nexthop":"10.0.1.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data.ref b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data.ref new file mode 100644 index 0000000000..4ef8d946f2 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data.ref @@ -0,0 +1,13 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "6.6.6.6", + "is-operational": false + } + ] + } + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref new file mode 100644 index 0000000000..9b28f6a42b --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref @@ -0,0 +1,20 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "6.6.6.6", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "discriminator": "*", + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref new file mode 100644 index 0000000000..9b28f6a42b --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref @@ -0,0 +1,20 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "6.6.6.6", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "discriminator": "*", + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref new file mode 100644 index 0000000000..249117198a --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref @@ -0,0 +1,25 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "6.6.6.6", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "discriminator": "*", + "is-best-candidate-path": false + }, + { + "preference": 200, + "discriminator": "*", + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table.ref new file mode 100644 index 0000000000..21f71f1254 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table.ref @@ -0,0 +1,20 @@ +{ + "1111":{ + "inLabel":1111, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":16020, + "outLabelStack":[ + 16020, + 16040, + 16060 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_add_segment.ref b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_add_segment.ref new file mode 100644 index 0000000000..3635c89efb --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_add_segment.ref @@ -0,0 +1,21 @@ +{ + "1111":{ + "inLabel":1111, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":16020, + "outLabelStack":[ + 16020, + 16040, + 16050, + 16060 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_change_segment.ref b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_change_segment.ref new file mode 100644 index 0000000000..5712d210d4 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_change_segment.ref @@ -0,0 +1,21 @@ +{ + "1111":{ + "inLabel":1111, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":16020, + "outLabelStack":[ + 16020, + 16040, + 16030, + 16060 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_active_srte.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_active_srte.ref new file mode 100644 index 0000000000..5a76246e50 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_active_srte.ref @@ -0,0 +1,29 @@ +{ + "9.9.9.2\/32":[ + { + "prefix":"9.9.9.2\/32", + "protocol":"bgp", + "installed":true, + "nexthops":[ + { + "ip":"6.6.6.6", + "afi":"ipv4", + "active":true, + "recursive":true, + "srteColor":1 + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16050, + 16060 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref new file mode 100644 index 0000000000..09d5958305 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref @@ -0,0 +1,38 @@ +{ + "9.9.9.2\/32":[ + { + "prefix":"9.9.9.2\/32", + "protocol":"bgp", + "installed":true, + "nexthops":[ + { + "ip":"6.6.6.6", + "afi":"ipv4", + "active":true, + "recursive":true, + "srteColor":1 + }, + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_active.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_active.ref new file mode 100644 index 0000000000..e26039b835 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_active.ref @@ -0,0 +1,20 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "6.6.6.6", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "discriminator": "*", + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_inactive.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_inactive.ref new file mode 100644 index 0000000000..01505c0318 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_inactive.ref @@ -0,0 +1,20 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "6.6.6.6", + "is-operational": false, + "candidate-path": [ + { + "preference": 100, + "discriminator": "*", + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt1/zebra.conf new file mode 100644 index 0000000000..9d71d3005f --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/zebra.conf @@ -0,0 +1,19 @@ +log file zebra.log +! +hostname rt1 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 + ipv6 address 2001:db8:1000::1/128 +! +interface eth-sw1 + ip address 10.0.1.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt2/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt2/isisd.conf new file mode 100644 index 0000000000..733f26bc62 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt2/isisd.conf @@ -0,0 +1,41 @@ +hostname rt2 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt4-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt4-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0002.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 20 no-php-flag + segment-routing prefix 2001:db8:1000::2/128 index 21 no-php-flag +! diff --git a/tests/topotests/isis-sr-te-topo1/rt2/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt2/zebra.conf new file mode 100644 index 0000000000..dcb0686dc2 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt2/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +! +hostname rt2 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 2.2.2.2/32 + ipv6 address 2001:db8:1000::2/128 +! +interface eth-sw1 + ip address 10.0.1.2/24 +! +interface eth-rt4-1 + ip address 10.0.2.2/24 +! +interface eth-rt4-2 + ip address 10.0.3.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt3/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt3/isisd.conf new file mode 100644 index 0000000000..2395906cbf --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt3/isisd.conf @@ -0,0 +1,41 @@ +hostname rt3 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt5-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt5-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0003.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 30 no-php-flag + segment-routing prefix 2001:db8:1000::3/128 index 31 no-php-flag +! diff --git a/tests/topotests/isis-sr-te-topo1/rt3/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt3/zebra.conf new file mode 100644 index 0000000000..3254529386 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt3/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +! +hostname rt3 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 3.3.3.3/32 + ipv6 address 2001:db8:1000::3/128 +! +interface eth-sw1 + ip address 10.0.1.3/24 +! +interface eth-rt5-1 + ip address 10.0.4.3/24 +! +interface eth-rt5-2 + ip address 10.0.5.3/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt4/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt4/isisd.conf new file mode 100644 index 0000000000..07a7867cbb --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt4/isisd.conf @@ -0,0 +1,48 @@ +hostname rt4 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt2-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0004.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 4.4.4.4/32 index 40 no-php-flag + segment-routing prefix 2001:db8:1000::4/128 index 41 no-php-flag +! diff --git a/tests/topotests/isis-sr-te-topo1/rt4/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt4/zebra.conf new file mode 100644 index 0000000000..4945897e9d --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt4/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log +! +hostname rt4 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 4.4.4.4/32 + ipv6 address 2001:db8:1000::4/128 +! +interface eth-rt2-1 + ip address 10.0.2.4/24 +! +interface eth-rt2-2 + ip address 10.0.3.4/24 +! +interface eth-rt5 + ip address 10.0.6.4/24 +! +interface eth-rt6 + ip address 10.0.7.4/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt5/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt5/isisd.conf new file mode 100644 index 0000000000..b0fcdede07 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt5/isisd.conf @@ -0,0 +1,48 @@ +hostname rt5 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt3-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt3-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0005.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 5.5.5.5/32 index 50 no-php-flag + segment-routing prefix 2001:db8:1000::5/128 index 51 no-php-flag +! diff --git a/tests/topotests/isis-sr-te-topo1/rt5/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt5/zebra.conf new file mode 100644 index 0000000000..4cfea1a59f --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt5/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log +! +hostname rt5 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 5.5.5.5/32 + ipv6 address 2001:db8:1000::5/128 +! +interface eth-rt3-1 + ip address 10.0.4.5/24 +! +interface eth-rt3-2 + ip address 10.0.5.5/24 +! +interface eth-rt4 + ip address 10.0.6.5/24 +! +interface eth-rt6 + ip address 10.0.8.5/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt6/bgpd.conf b/tests/topotests/isis-sr-te-topo1/rt6/bgpd.conf new file mode 100644 index 0000000000..e72ee52fce --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/bgpd.conf @@ -0,0 +1,12 @@ +log file bgpd.log +! +router bgp 1 + bgp router-id 6.6.6.6 + neighbor 1.1.1.1 remote-as 1 + neighbor 1.1.1.1 update-source lo + ! + address-family ipv4 unicast + redistribute static + neighbor 1.1.1.1 next-hop-self + exit-address-family +! diff --git a/tests/topotests/isis-sr-te-topo1/rt6/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt6/isisd.conf new file mode 100644 index 0000000000..3be24ad24c --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/isisd.conf @@ -0,0 +1,36 @@ +hostname rt6 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0006.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 6.6.6.6/32 index 60 + segment-routing prefix 2001:db8:1000::6/128 index 61 +! diff --git a/tests/topotests/isis-sr-te-topo1/rt6/pathd.conf b/tests/topotests/isis-sr-te-topo1/rt6/pathd.conf new file mode 100644 index 0000000000..3bada7147c --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/pathd.conf @@ -0,0 +1,21 @@ +log file pathd.log +! +hostname rt6 +! +segment-routing + traffic-eng + segment-list default + index 10 mpls label 16020 + index 20 mpls label 16010 + ! + segment-list test + index 10 mpls label 16050 + index 20 mpls label 16030 + index 30 mpls label 16010 + ! + policy color 1 endpoint 1.1.1.1 + name default + binding-sid 6666 + ! + ! +! diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref new file mode 100644 index 0000000000..2bb000346f --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref @@ -0,0 +1,91 @@ +{ + "6666":{ + "inLabel":6666, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":16020, + "outLabelStack":[ + 16020, + 16010 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16010": { + "inLabel": 16010, + "installed": true, + "nexthops": [ + { + "distance": 150, + "installed": true, + "nexthop": "10.0.7.4", + "outLabel": 16010, + "type": "SR (IS-IS)" + }, + { + "distance": 150, + "installed": true, + "nexthop": "10.0.8.5", + "outLabel": 16010, + "type": "SR (IS-IS)" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref new file mode 100644 index 0000000000..348f7761eb --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref @@ -0,0 +1,74 @@ +{ + "16010": { + "inLabel": 16010, + "installed": true, + "nexthops": [ + { + "distance": 150, + "installed": true, + "nexthop": "10.0.7.4", + "outLabel": 16010, + "type": "SR (IS-IS)" + }, + { + "distance": 150, + "installed": true, + "nexthop": "10.0.8.5", + "outLabel": 16010, + "type": "SR (IS-IS)" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data.ref b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data.ref new file mode 100644 index 0000000000..241c80bdd7 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data.ref @@ -0,0 +1,13 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "1.1.1.1", + "is-operational": false + } + ] + } + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref new file mode 100644 index 0000000000..20ea69e386 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref @@ -0,0 +1,19 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "1.1.1.1", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref new file mode 100644 index 0000000000..20ea69e386 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref @@ -0,0 +1,19 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "1.1.1.1", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref new file mode 100644 index 0000000000..10cafe9091 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref @@ -0,0 +1,23 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "1.1.1.1", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "is-best-candidate-path": false + }, + { + "preference": 200, + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step4/show_mpls_table.ref b/tests/topotests/isis-sr-te-topo1/rt6/step4/show_mpls_table.ref new file mode 100644 index 0000000000..95bf995e2e --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step4/show_mpls_table.ref @@ -0,0 +1,20 @@ +{ + "6666":{ + "inLabel":6666, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":16050, + "outLabelStack":[ + 16050, + 16030, + 16010 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt6/zebra.conf new file mode 100644 index 0000000000..32c6e6c4e0 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/zebra.conf @@ -0,0 +1,27 @@ +log file zebra.log +! +hostname rt6 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 6.6.6.6/32 + ipv6 address 2001:db8:1000::6/128 +! +interface eth-rt4 + ip address 10.0.7.6/24 +! +interface eth-rt5 + ip address 10.0.8.6/24 +! +interface eth-dst + ip address 10.0.11.1/24 +! +ip forwarding +! +ip route 9.9.9.2/32 10.0.11.2 +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py b/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py new file mode 100755 index 0000000000..bfd2f92a28 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py @@ -0,0 +1,532 @@ +#!/usr/bin/env python + +# +# test_isis_sr_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# 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_isis_sr_te_topo1.py: + + +---------+ + | | + | RT1 | + | 1.1.1.1 | + | | + +---------+ + |eth-sw1 + | + | + | + +---------+ | +---------+ + | | | | | + | RT2 |eth-sw1 | eth-sw1| RT3 | + | 2.2.2.2 +----------+----------+ 3.3.3.3 | + | | 10.0.1.0/24 | | + +---------+ +---------+ + eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2 + | | | | + 10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24 + | | | | + eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2 + +---------+ +---------+ + | | | | + | RT4 | 10.0.6.0/24 | RT5 | + | 4.4.4.4 +---------------------+ 5.5.5.5 | + | |eth-rt5 eth-rt4| | + +---------+ +---------+ + eth-rt6| |eth-rt6 + | | + 10.0.7.0/24| |10.0.8.0/24 + | +---------+ | + | | | | + | | RT6 | | + +----------+ 6.6.6.6 +-----------+ + eth-rt4| |eth-rt5 + +---------+ + |eth-dst (.1) + | + |10.0.11.0/24 + | + |eth-rt6 (.2) + +---------+ + | | + | DST | + | 9.9.9.2 | + | | + +---------+ + +""" + +import os +import sys +import pytest +import json +import re +from time import sleep +from functools import partial + +# 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 + +class TemplateTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'dst']: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['rt1'], nodeif="eth-sw1") + switch.add_link(tgen.gears['rt2'], nodeif="eth-sw1") + switch.add_link(tgen.gears['rt3'], nodeif="eth-sw1") + + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['rt2'], nodeif="eth-rt4-1") + switch.add_link(tgen.gears['rt4'], nodeif="eth-rt2-1") + + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['rt2'], nodeif="eth-rt4-2") + switch.add_link(tgen.gears['rt4'], nodeif="eth-rt2-2") + + switch = tgen.add_switch('s4') + switch.add_link(tgen.gears['rt3'], nodeif="eth-rt5-1") + switch.add_link(tgen.gears['rt5'], nodeif="eth-rt3-1") + + switch = tgen.add_switch('s5') + switch.add_link(tgen.gears['rt3'], nodeif="eth-rt5-2") + switch.add_link(tgen.gears['rt5'], nodeif="eth-rt3-2") + + switch = tgen.add_switch('s6') + switch.add_link(tgen.gears['rt4'], nodeif="eth-rt5") + switch.add_link(tgen.gears['rt5'], nodeif="eth-rt4") + + switch = tgen.add_switch('s7') + switch.add_link(tgen.gears['rt4'], nodeif="eth-rt6") + switch.add_link(tgen.gears['rt6'], nodeif="eth-rt4") + + switch = tgen.add_switch('s8') + switch.add_link(tgen.gears['rt5'], nodeif="eth-rt6") + switch.add_link(tgen.gears['rt6'], nodeif="eth-rt5") + + switch = tgen.add_switch('s9') + switch.add_link(tgen.gears['rt6'], nodeif="eth-dst") + switch.add_link(tgen.gears['dst'], nodeif="eth-rt6") + +@pytest.mark.isis +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(TemplateTopo, mod.__name__) + + frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir") + if not os.path.isfile(os.path.join(frrdir, "pathd")): + pytest.skip("pathd daemon wasn't built") + + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + 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_ISIS, + os.path.join(CWD, '{}/isisd.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_PATH, + os.path.join(CWD, '{}/pathd.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + tgen.start_router() + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + +def setup_testcase(msg): + logger.info(msg) + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + return tgen + +def print_cmd_result(rname, command): + print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False)) + +def compare_json_test(router, command, reference, exact): + output = router.vtysh_cmd(command, isjson=True) + result = topotest.json_cmp(output, reference) + + # Note: topotest.json_cmp() just checks on inclusion of keys. + # For exact matching also compare the other way around. + if not result and exact: + return topotest.json_cmp(reference, output) + else: + return result + +def cmp_json_output(rname, command, reference, exact=False): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = '{}/{}/{}'.format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(compare_json_test, + tgen.gears[rname], command, expected, exact) + _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + +def cmp_json_output_exact(rname, command, reference): + return cmp_json_output(rname, command, reference, True) + +def add_candidate_path(rname, endpoint, pref, name, segment_list='default'): + get_topogen().net[rname].cmd(''' \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "policy color 1 endpoint ''' + endpoint + '''" \ + -c "candidate-path preference ''' + str(pref) + ''' name ''' + name + ''' explicit segment-list ''' + segment_list + '''"''') + +def delete_candidate_path(rname, endpoint, pref): + get_topogen().net[rname].cmd(''' \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "policy color 1 endpoint ''' + endpoint + '''" \ + -c "no candidate-path preference ''' + str(pref) + '''"''') + +def add_segment(rname, name, index, label): + get_topogen().net[rname].cmd(''' \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "segment-list ''' + name + '''" \ + -c "index ''' + str(index) + ''' mpls label ''' + str(label) + '''"''') + +def delete_segment(rname, name, index): + get_topogen().net[rname].cmd(''' \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "segment-list ''' + name + '''" \ + -c "no index ''' + str(index) + '''"''') + +def create_sr_policy(rname, endpoint, bsid): + get_topogen().net[rname].cmd(''' \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "policy color 1 endpoint ''' + endpoint + '''" \ + -c "name default" \ + -c "binding-sid ''' + str(bsid) + '''"''') + +def delete_sr_policy(rname, endpoint): + get_topogen().net[rname].cmd(''' \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "no policy color 1 endpoint ''' + endpoint + '''"''') + +def create_prefix_sid(rname, prefix, sid): + get_topogen().net[rname].cmd(''' \ + vtysh -c "conf t" \ + -c "router isis 1" \ + -c "segment-routing prefix ''' + prefix + " index " + str(sid) + '''"''') + +def delete_prefix_sid(rname, prefix): + get_topogen().net[rname].cmd(''' \ + vtysh -c "conf t" \ + -c "router isis 1" \ + -c "no segment-routing prefix "''' + prefix) + +# +# Step 1 +# +# Checking the MPLS table using a single SR Policy and a single Candidate Path +# +def test_srte_init_step1(): + setup_testcase("Test (step 1): wait for IS-IS convergence / label distribution") + + for rname in ['rt1', 'rt6']: + cmp_json_output(rname, + "show mpls table json", + "step1/show_mpls_table_without_candidate.ref") + +def test_srte_add_candidate_check_mpls_table_step1(): + setup_testcase("Test (step 1): check MPLS table regarding the added Candidate Path") + + for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: + add_candidate_path(rname, endpoint, 100, 'default') + cmp_json_output(rname, + "show mpls table json", + "step1/show_mpls_table_with_candidate.ref") + delete_candidate_path(rname, endpoint, 100) + +def test_srte_reinstall_sr_policy_check_mpls_table_step1(): + setup_testcase("Test (step 1): check MPLS table after the SR Policy was removed and reinstalled") + + for rname, endpoint, bsid in [('rt1', '6.6.6.6', 1111), ('rt6', '1.1.1.1', 6666)]: + add_candidate_path(rname, endpoint, 100, 'default') + delete_sr_policy(rname, endpoint) + cmp_json_output(rname, + "show mpls table json", + "step1/show_mpls_table_without_candidate.ref") + create_sr_policy(rname, endpoint, bsid) + add_candidate_path(rname, endpoint, 100, 'default') + cmp_json_output(rname, + "show mpls table json", + "step1/show_mpls_table_with_candidate.ref") + delete_candidate_path(rname, endpoint, 100) + +# +# Step 2 +# +# Checking pathd operational data using a single SR Policy and a single Candidate Path +# +def test_srte_bare_policy_step2(): + setup_testcase("Test (step 2): bare SR Policy should not be operational") + + for rname in ['rt1', 'rt6']: + cmp_json_output_exact(rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step2/show_operational_data.ref") + +def test_srte_add_candidate_check_operational_data_step2(): + setup_testcase("Test (step 2): add single Candidate Path, SR Policy should be operational") + + for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: + add_candidate_path(rname, endpoint, 100, 'default') + cmp_json_output(rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step2/show_operational_data_with_candidate.ref") + +def test_srte_config_remove_candidate_check_operational_data_step2(): + setup_testcase("Test (step 2): remove single Candidate Path, SR Policy should not be operational anymore") + + for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: + delete_candidate_path(rname, endpoint, 100) + cmp_json_output_exact(rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step2/show_operational_data.ref") + +# +# Step 3 +# +# Testing the Candidate Path selection +# +def test_srte_add_two_candidates_step3(): + setup_testcase("Test (step 3): second Candidate Path has higher Priority") + + for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: + for pref, cand_name in [('100', 'first'), ('200', 'second')]: + add_candidate_path(rname, endpoint, pref, cand_name) + cmp_json_output(rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step3/show_operational_data_with_two_candidates.ref") + + # cleanup + for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: + for pref in ['100', '200']: + delete_candidate_path(rname, endpoint, pref) + +def test_srte_add_two_candidates_with_reverse_priority_step3(): + setup_testcase("Test (step 3): second Candidate Path has lower Priority") + + # Use reversed priorities here + for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: + for pref, cand_name in [('200', 'first'), ('100', 'second')]: + add_candidate_path(rname, endpoint, pref, cand_name) + cmp_json_output(rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step3/show_operational_data_with_two_candidates.ref") + + # cleanup + for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: + for pref in ['100', '200']: + delete_candidate_path(rname, endpoint, pref) + +def test_srte_remove_best_candidate_step3(): + setup_testcase("Test (step 3): delete the Candidate Path with higher priority") + + for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: + for pref, cand_name in [('100', 'first'), ('200', 'second')]: + add_candidate_path(rname, endpoint, pref, cand_name) + + # Delete candidate with higher priority + for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: + delete_candidate_path(rname, endpoint, 200) + + # Candidate with lower priority should get active now + for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: + cmp_json_output(rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step3/show_operational_data_with_single_candidate.ref") + # cleanup + delete_candidate_path(rname, endpoint, 100) + +# +# Step 4 +# +# Checking MPLS table with a single SR Policy and a Candidate Path with different Segment Lists and other modifications +# +def test_srte_change_segment_list_check_mpls_table_step4(): + setup_testcase("Test (step 4): check MPLS table for changed Segment List") + + for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: + add_candidate_path(rname, endpoint, 100, 'default') + # now change the segment list name + add_candidate_path(rname, endpoint, 100, 'default', 'test') + cmp_json_output(rname, + "show mpls table json", + "step4/show_mpls_table.ref") + delete_candidate_path(rname, endpoint, 100) + +def test_srte_segment_list_add_segment_check_mpls_table_step4(): + setup_testcase("Test (step 4): check MPLS table for added (then changed and finally deleted) segment") + + add_candidate_path('rt1', '6.6.6.6', 100, 'default', 'test') + + # first add a new segment + add_segment('rt1', 'test', 25, 16050) + cmp_json_output('rt1', + "show mpls table json", + "step4/show_mpls_table_add_segment.ref") + + # ... then change it ... + add_segment('rt1', 'test', 25, 16030) + cmp_json_output('rt1', + "show mpls table json", + "step4/show_mpls_table_change_segment.ref") + + # ... and finally delete it + delete_segment('rt1', 'test', 25) + cmp_json_output('rt1', + "show mpls table json", + "step4/show_mpls_table.ref") + delete_candidate_path('rt1', '6.6.6.6', 100) + +# +# Step 5 +# +# Checking the nexthop using a single SR Policy and a Candidate Path with configured route-map +# +def test_srte_route_map_with_sr_policy_check_nextop_step5(): + setup_testcase("Test (step 5): recursive nexthop learned through BGP neighbour should be aligned with SR Policy from route-map") + + # (re-)build the SR Policy two times to ensure that reinstalling still works + for i in [1,2]: + cmp_json_output('rt1', + "show ip route bgp json", + "step5/show_ip_route_bgp_inactive_srte.ref") + + delete_sr_policy('rt1', '6.6.6.6') + cmp_json_output('rt1', + "show ip route bgp json", + "step5/show_ip_route_bgp_inactive_srte.ref") + + create_sr_policy('rt1', '6.6.6.6', 1111) + cmp_json_output('rt1', + "show ip route bgp json", + "step5/show_ip_route_bgp_inactive_srte.ref") + + add_candidate_path('rt1', '6.6.6.6', 100, 'default') + cmp_json_output('rt1', + "show ip route bgp json", + "step5/show_ip_route_bgp_active_srte.ref") + + delete_candidate_path('rt1', '6.6.6.6', 100) + +def test_srte_route_map_with_sr_policy_reinstall_prefix_sid_check_nextop_step5(): + setup_testcase("Test (step 5): remove and re-install prefix SID on fist path element and check SR Policy activity") + + # first add a candidate path so the SR Policy is active + add_candidate_path('rt1', '6.6.6.6', 100, 'default') + cmp_json_output('rt1', + "show yang operational-data /frr-pathd:pathd pathd", + "step5/show_operational_data_active.ref") + + # delete prefix SID from first element of the configured path and check + # if the SR Policy is inactive since the label can't be resolved anymore + delete_prefix_sid('rt5', "5.5.5.5/32") + cmp_json_output('rt1', + "show yang operational-data /frr-pathd:pathd pathd", + "step5/show_operational_data_inactive.ref") + cmp_json_output('rt1', + "show ip route bgp json", + "step5/show_ip_route_bgp_inactive_srte.ref") + + # re-create the prefix SID and check if the SR Policy is active + create_prefix_sid('rt5', "5.5.5.5/32", 50) + cmp_json_output('rt1', + "show yang operational-data /frr-pathd:pathd pathd", + "step5/show_operational_data_active.ref") + cmp_json_output('rt1', + "show ip route bgp json", + "step5/show_ip_route_bgp_active_srte.ref") + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py b/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py index 34eb6d90f6..63be3f78aa 100644 --- a/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py +++ b/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py @@ -134,7 +134,7 @@ class TemplateTopo(Topo): switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6") switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5") - +@pytest.mark.isis def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(TemplateTopo, mod.__name__) diff --git a/tests/topotests/isis-tilfa-topo1/test_isis_tilfa_topo1.py b/tests/topotests/isis-tilfa-topo1/test_isis_tilfa_topo1.py index 514ea53552..83751fabcd 100755 --- a/tests/topotests/isis-tilfa-topo1/test_isis_tilfa_topo1.py +++ b/tests/topotests/isis-tilfa-topo1/test_isis_tilfa_topo1.py @@ -74,7 +74,7 @@ from functools import partial # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers @@ -88,8 +88,10 @@ from mininet.topo import Topo # Global multi-dimensional dictionary containing all expected outputs outputs = {} + class TemplateTopo(Topo): "Test topology builder" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) @@ -97,80 +99,85 @@ class TemplateTopo(Topo): # # Define FRR Routers # - for router in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: tgen.add_router(router) # # Define connections # - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['rt1'], nodeif="eth-sw1") - switch.add_link(tgen.gears['rt2'], nodeif="eth-sw1") - switch.add_link(tgen.gears['rt3'], nodeif="eth-sw1") + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1") + switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1") + switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1") - switch = tgen.add_switch('s2') - switch.add_link(tgen.gears['rt2'], nodeif="eth-rt4-1") - switch.add_link(tgen.gears['rt4'], nodeif="eth-rt2-1") + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-1") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-1") - switch = tgen.add_switch('s3') - switch.add_link(tgen.gears['rt2'], nodeif="eth-rt4-2") - switch.add_link(tgen.gears['rt4'], nodeif="eth-rt2-2") + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-2") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-2") - switch = tgen.add_switch('s4') - switch.add_link(tgen.gears['rt3'], nodeif="eth-rt5-1") - switch.add_link(tgen.gears['rt5'], nodeif="eth-rt3-1") + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-1") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-1") - switch = tgen.add_switch('s5') - switch.add_link(tgen.gears['rt3'], nodeif="eth-rt5-2") - switch.add_link(tgen.gears['rt5'], nodeif="eth-rt3-2") + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-2") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-2") - switch = tgen.add_switch('s6') - switch.add_link(tgen.gears['rt4'], nodeif="eth-rt5") - switch.add_link(tgen.gears['rt5'], nodeif="eth-rt4") + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4") - switch = tgen.add_switch('s7') - switch.add_link(tgen.gears['rt4'], nodeif="eth-rt6") - switch.add_link(tgen.gears['rt6'], nodeif="eth-rt4") + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4") - switch = tgen.add_switch('s8') - switch.add_link(tgen.gears['rt5'], nodeif="eth-rt6") - switch.add_link(tgen.gears['rt6'], nodeif="eth-rt5") + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5") # # Populate multi-dimensional dictionary containing all expected outputs # - files = ["show_ip_route.ref", - "show_ipv6_route.ref", - "show_mpls_table.ref", - "show_yang_interface_isis_adjacencies.ref"] - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + files = [ + "show_ip_route.ref", + "show_ipv6_route.ref", + "show_mpls_table.ref", + "show_yang_interface_isis_adjacencies.ref", + ] + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: outputs[rname] = {} for step in range(1, 9 + 1): outputs[rname][step] = {} for file in files: if step == 1: # Get snapshots relative to the expected initial network convergence - filename = '{}/{}/step{}/{}'.format(CWD, rname, step, file) + filename = "{}/{}/step{}/{}".format(CWD, rname, step, file) outputs[rname][step][file] = open(filename).read() else: if file == "show_yang_interface_isis_adjacencies.ref": continue # Get diff relative to the previous step - filename = '{}/{}/step{}/{}.diff'.format(CWD, rname, step, file) + filename = "{}/{}/step{}/{}.diff".format(CWD, rname, step, file) # Create temporary files in order to apply the diff f_in = tempfile.NamedTemporaryFile() f_in.write(outputs[rname][step - 1][file]) f_in.flush() f_out = tempfile.NamedTemporaryFile() - os.system("patch -s -o %s %s %s" %(f_out.name, f_in.name, filename)) + os.system( + "patch -s -o %s %s %s" % (f_out.name, f_in.name, filename) + ) # Store the updated snapshot and remove the temporary files outputs[rname][step][file] = open(f_out.name).read() f_in.close() f_out.close() +@pytest.mark.isis def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(TemplateTopo, mod.__name__) @@ -181,16 +188,15 @@ def setup_module(mod): # For all registered routers, load the zebra configuration file for rname, router in router_list.items(): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_ISIS, - os.path.join(CWD, '{}/isisd.conf'.format(rname)) + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) ) tgen.start_router() + def teardown_module(mod): "Teardown the pytest environment" tgen = get_topogen() @@ -198,6 +204,7 @@ def teardown_module(mod): # This function tears down the whole topology. tgen.stop_topology() + def router_compare_json_output(rname, command, reference): "Compare router JSON output" @@ -207,12 +214,12 @@ def router_compare_json_output(rname, command, reference): expected = json.loads(reference) # Run test function until we get an result. Wait at most 60 seconds. - test_func = partial(topotest.router_json_cmp, - tgen.gears[rname], command, expected) + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5) assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) assert diff is None, assertmsg + # # Step 1 # @@ -226,9 +233,13 @@ def test_isis_adjacencies_step1(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", - outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, + "show yang operational-data /frr-interface:lib isisd", + outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"], + ) + def test_rib_ipv4_step1(): logger.info("Test (step 1): verify IPv4 RIB") @@ -238,9 +249,11 @@ def test_rib_ipv4_step1(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ip route isis json", - outputs[rname][1]["show_ip_route.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][1]["show_ip_route.ref"] + ) + def test_rib_ipv6_step1(): logger.info("Test (step 1): verify IPv6 RIB") @@ -250,9 +263,11 @@ def test_rib_ipv6_step1(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][1]["show_ipv6_route.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][1]["show_ipv6_route.ref"] + ) + def test_mpls_lib_step1(): logger.info("Test (step 1): verify MPLS LIB") @@ -262,9 +277,11 @@ def test_mpls_lib_step1(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show mpls table json", - outputs[rname][1]["show_mpls_table.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show mpls table json", outputs[rname][1]["show_mpls_table.ref"] + ) + # # Step 2 @@ -283,12 +300,16 @@ def test_rib_ipv4_step2(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Disabling TI-LFA link protection on rt2\'s eth-sw1 interface') - tgen.net['rt2'].cmd('vtysh -c "conf t" -c "interface eth-sw1" -c "no isis fast-reroute ti-lfa"') + logger.info("Disabling TI-LFA link protection on rt2's eth-sw1 interface") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "interface eth-sw1" -c "no isis fast-reroute ti-lfa"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][2]["show_ip_route.ref"] + ) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ip route isis json", - outputs[rname][2]["show_ip_route.ref"]) def test_rib_ipv6_step2(): logger.info("Test (step 2): verify IPv6 RIB") @@ -298,9 +319,11 @@ def test_rib_ipv6_step2(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][2]["show_ipv6_route.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][2]["show_ipv6_route.ref"] + ) + def test_mpls_lib_step2(): logger.info("Test (step 2): verify MPLS LIB") @@ -310,9 +333,11 @@ def test_mpls_lib_step2(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show mpls table json", - outputs[rname][2]["show_mpls_table.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show mpls table json", outputs[rname][2]["show_mpls_table.ref"] + ) + # # Step 3 @@ -331,12 +356,16 @@ def test_rib_ipv4_step3(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Enabling TI-LFA link protection on rt2\'s eth-sw1 interface') - tgen.net['rt2'].cmd('vtysh -c "conf t" -c "interface eth-sw1" -c "isis fast-reroute ti-lfa"') + logger.info("Enabling TI-LFA link protection on rt2's eth-sw1 interface") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "interface eth-sw1" -c "isis fast-reroute ti-lfa"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][3]["show_ip_route.ref"] + ) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ip route isis json", - outputs[rname][3]["show_ip_route.ref"]) def test_rib_ipv6_step3(): logger.info("Test (step 3): verify IPv6 RIB") @@ -346,9 +375,11 @@ def test_rib_ipv6_step3(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][3]["show_ipv6_route.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][3]["show_ipv6_route.ref"] + ) + def test_mpls_lib_step3(): logger.info("Test (step 3): verify MPLS LIB") @@ -358,9 +389,11 @@ def test_mpls_lib_step3(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show mpls table json", - outputs[rname][3]["show_mpls_table.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show mpls table json", outputs[rname][3]["show_mpls_table.ref"] + ) + # # Step 4 @@ -384,12 +417,16 @@ def test_rib_ipv4_step4(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Disabling SR on rt4') - tgen.net['rt4'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no segment-routing on"') + logger.info("Disabling SR on rt4") + tgen.net["rt4"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "no segment-routing on"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][4]["show_ip_route.ref"] + ) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ip route isis json", - outputs[rname][4]["show_ip_route.ref"]) def test_rib_ipv6_step4(): logger.info("Test (step 4): verify IPv6 RIB") @@ -399,9 +436,11 @@ def test_rib_ipv6_step4(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][4]["show_ipv6_route.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][4]["show_ipv6_route.ref"] + ) + def test_mpls_lib_step4(): logger.info("Test (step 4): verify MPLS LIB") @@ -411,9 +450,11 @@ def test_mpls_lib_step4(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show mpls table json", - outputs[rname][4]["show_mpls_table.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show mpls table json", outputs[rname][4]["show_mpls_table.ref"] + ) + # # Step 5 @@ -432,12 +473,14 @@ def test_rib_ipv4_step5(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Enabling SR on rt4') - tgen.net['rt4'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing on"') + logger.info("Enabling SR on rt4") + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing on"') + + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][5]["show_ip_route.ref"] + ) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ip route isis json", - outputs[rname][5]["show_ip_route.ref"]) def test_rib_ipv6_step5(): logger.info("Test (step 5): verify IPv6 RIB") @@ -447,9 +490,11 @@ def test_rib_ipv6_step5(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][5]["show_ipv6_route.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][5]["show_ipv6_route.ref"] + ) + def test_mpls_lib_step5(): logger.info("Test (step 5): verify MPLS LIB") @@ -459,9 +504,11 @@ def test_mpls_lib_step5(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show mpls table json", - outputs[rname][5]["show_mpls_table.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show mpls table json", outputs[rname][5]["show_mpls_table.ref"] + ) + # # Step 6 @@ -480,12 +527,16 @@ def test_rib_ipv4_step6(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Changing rt5\'s SRGB') - tgen.net['rt5'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing global-block 30000 37999"') + logger.info("Changing rt5's SRGB") + tgen.net["rt5"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing global-block 30000 37999"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][6]["show_ip_route.ref"] + ) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ip route isis json", - outputs[rname][6]["show_ip_route.ref"]) def test_rib_ipv6_step6(): logger.info("Test (step 6): verify IPv6 RIB") @@ -495,9 +546,11 @@ def test_rib_ipv6_step6(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][6]["show_ipv6_route.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][6]["show_ipv6_route.ref"] + ) + def test_mpls_lib_step6(): logger.info("Test (step 6): verify MPLS LIB") @@ -507,9 +560,11 @@ def test_mpls_lib_step6(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show mpls table json", - outputs[rname][6]["show_mpls_table.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show mpls table json", outputs[rname][6]["show_mpls_table.ref"] + ) + # # Step 7 @@ -529,13 +584,19 @@ def test_rib_ipv4_step7(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Deleting rt5\'s Prefix-SIDs') - tgen.net['rt5'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no segment-routing prefix 5.5.5.5/32 index 50"') - tgen.net['rt5'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no segment-routing prefix 2001:db8:1000::5/128 index 51"') + logger.info("Deleting rt5's Prefix-SIDs") + tgen.net["rt5"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "no segment-routing prefix 5.5.5.5/32 index 50"' + ) + tgen.net["rt5"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "no segment-routing prefix 2001:db8:1000::5/128 index 51"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][7]["show_ip_route.ref"] + ) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ip route isis json", - outputs[rname][7]["show_ip_route.ref"]) def test_rib_ipv6_step7(): logger.info("Test (step 7): verify IPv6 RIB") @@ -545,9 +606,11 @@ def test_rib_ipv6_step7(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][7]["show_ipv6_route.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][7]["show_ipv6_route.ref"] + ) + def test_mpls_lib_step7(): logger.info("Test (step 7): verify MPLS LIB") @@ -557,9 +620,11 @@ def test_mpls_lib_step7(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show mpls table json", - outputs[rname][7]["show_mpls_table.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show mpls table json", outputs[rname][7]["show_mpls_table.ref"] + ) + # # Step 8 @@ -578,13 +643,19 @@ def test_rib_ipv4_step8(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Re-adding rt5\'s Prefix-SIDs') - tgen.net['rt5'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 5.5.5.5/32 index 50"') - tgen.net['rt5'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::5/128 index 51"') + logger.info("Re-adding rt5's Prefix-SIDs") + tgen.net["rt5"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 5.5.5.5/32 index 50"' + ) + tgen.net["rt5"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::5/128 index 51"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][8]["show_ip_route.ref"] + ) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ip route isis json", - outputs[rname][8]["show_ip_route.ref"]) def test_rib_ipv6_step8(): logger.info("Test (step 8): verify IPv6 RIB") @@ -594,9 +665,11 @@ def test_rib_ipv6_step8(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][8]["show_ipv6_route.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][8]["show_ipv6_route.ref"] + ) + def test_mpls_lib_step8(): logger.info("Test (step 8): verify MPLS LIB") @@ -606,9 +679,11 @@ def test_mpls_lib_step8(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show mpls table json", - outputs[rname][8]["show_mpls_table.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show mpls table json", outputs[rname][8]["show_mpls_table.ref"] + ) + # # Step 9 @@ -628,13 +703,19 @@ def test_rib_ipv4_step9(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Re-adding rt5\'s Prefix-SIDs') - tgen.net['rt5'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 5.5.5.5/32 index 500"') - tgen.net['rt5'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::5/128 index 501"') + logger.info("Re-adding rt5's Prefix-SIDs") + tgen.net["rt5"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 5.5.5.5/32 index 500"' + ) + tgen.net["rt5"].cmd( + 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::5/128 index 501"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ip route isis json", outputs[rname][9]["show_ip_route.ref"] + ) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ip route isis json", - outputs[rname][9]["show_ip_route.ref"]) def test_rib_ipv6_step9(): logger.info("Test (step 9): verify IPv6 RIB") @@ -644,9 +725,11 @@ def test_rib_ipv6_step9(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show ipv6 route isis json", - outputs[rname][9]["show_ipv6_route.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show ipv6 route isis json", outputs[rname][9]["show_ipv6_route.ref"] + ) + def test_mpls_lib_step9(): logger.info("Test (step 9): verify MPLS LIB") @@ -656,19 +739,22 @@ def test_mpls_lib_step9(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: - router_compare_json_output(rname, "show mpls table json", - outputs[rname][9]["show_mpls_table.ref"]) + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: + router_compare_json_output( + rname, "show mpls table json", outputs[rname][9]["show_mpls_table.ref"] + ) + # Memory leak test template def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') + pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() -if __name__ == '__main__': + +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py index 12121e4ddf..a79c8a268b 100644 --- a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py +++ b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py @@ -82,7 +82,7 @@ class ISISTopo1(Topo): sw.add_link(tgen.gears["r4"]) sw.add_link(tgen.gears["r5"]) - +@pytest.mark.isis def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(ISISTopo1, mod.__name__) diff --git a/tests/topotests/isis-topo1/test_isis_topo1.py b/tests/topotests/isis-topo1/test_isis_topo1.py index 71005a0362..25116d9452 100644 --- a/tests/topotests/isis-topo1/test_isis_topo1.py +++ b/tests/topotests/isis-topo1/test_isis_topo1.py @@ -84,7 +84,7 @@ class ISISTopo1(Topo): sw.add_link(tgen.gears["r4"]) sw.add_link(tgen.gears["r5"]) - +@pytest.mark.isis def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(ISISTopo1, mod.__name__) diff --git a/tests/topotests/ldp-topo1/test_ldp_topo1.py b/tests/topotests/ldp-topo1/test_ldp_topo1.py index 31adeafbf6..dfe65f010e 100644 --- a/tests/topotests/ldp-topo1/test_ldp_topo1.py +++ b/tests/topotests/ldp-topo1/test_ldp_topo1.py @@ -159,7 +159,7 @@ class NetworkTopo(Topo): ## ##################################################### - +@pytest.mark.ldp def setup_module(module): global topo, net global fatal_error @@ -647,9 +647,11 @@ def test_zebra_ipv4_routingTable(): else: print("r%s ok" % i) - assert failures == 0, ( - "IPv4 Zebra Routing Table verification failed for router r%s:\n%s" - % (i, diff) + assert ( + failures == 0 + ), "IPv4 Zebra Routing Table verification failed for router r%s:\n%s" % ( + i, + diff, ) # Make sure that all daemons are running diff --git a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py index ba94cd47d4..d659acb470 100644 --- a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py +++ b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py @@ -121,7 +121,8 @@ class TemplateTopo(Topo): switch.add_link(tgen.gears["r2"]) switch.add_link(tgen.gears["r3"]) - +@pytest.mark.ldp +@pytest.mark.ospf def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(TemplateTopo, mod.__name__) diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index ddeaf55b33..22602cb460 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -21,6 +21,7 @@ from copy import deepcopy from time import sleep import traceback +import ipaddr import ipaddress import os import sys @@ -691,8 +692,8 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True): config_data.append("{} activate".format(neigh_cxt)) disable_connected = peer.setdefault("disable_connected_check", False) - keep_alive = peer.setdefault("keepalivetimer", 60) - hold_down = peer.setdefault("holddowntimer", 180) + keep_alive = peer.setdefault("keepalivetimer", 3) + hold_down = peer.setdefault("holddowntimer", 10) password = peer.setdefault("password", None) no_password = peer.setdefault("no_password", None) max_hop_limit = peer.setdefault("ebgp_multihop", 1) @@ -1615,8 +1616,6 @@ def clear_bgp(tgen, addr_type, router, vrf=None): else: run_frr_cmd(rnode, "clear bgp *") - sleep(5) - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) @@ -2115,8 +2114,8 @@ def verify_bgp_attributes( errormsg(str) or True """ - logger.debug("Entering lib API: verify_bgp_attributes()") - for router, rnode in tgen.routers().items(): + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + for router, rnode in tgen.routers().iteritems(): if router != dut: continue @@ -2129,7 +2128,9 @@ def verify_bgp_attributes( dict_to_test = [] tmp_list = [] - if "route_maps" in input_dict.values()[0]: + dict_list = list(input_dict.values())[0] + + if "route_maps" in dict_list: for rmap_router in input_dict.keys(): for rmap, values in input_dict[rmap_router]["route_maps"].items(): if rmap == rmap_name: @@ -2194,7 +2195,7 @@ def verify_bgp_attributes( ) return errormsg - logger.debug("Exiting lib API: verify_bgp_attributes()") + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True @@ -2514,8 +2515,9 @@ def verify_best_path_as_per_admin_distance( return True -@retry(attempts=6, wait=2, return_is_str=True) -def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None): +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, +aspath=None, multi_nh=None): """ This API is to verify whether bgp rib has any matching route for a nexthop. @@ -2550,6 +2552,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) additional_nexthops_in_required_nhs = [] list1 = [] list2 = [] + found_hops = [] for routerInput in input_dict.keys(): for router, rnode in router_list.items(): if router != dut: @@ -2616,44 +2619,73 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) st_found = True found_routes.append(st_rt) - if next_hop: + if next_hop and multi_nh and st_found: if not isinstance(next_hop, list): next_hop = [next_hop] list1 = next_hop - found_hops = [ - rib_r["ip"] - for rib_r in rib_routes_json["routes"][st_rt][0][ - "nexthops" - ] - ] - list2 = found_hops - - missing_list_of_nexthops = set(list2).difference(list1) - additional_nexthops_in_required_nhs = set( - list1 - ).difference(list2) + for mnh in range( + 0, len(rib_routes_json["routes"][st_rt]) + ): + found_hops.append( + [ + rib_r["ip"] + for rib_r in rib_routes_json["routes"][ + st_rt + ][mnh]["nexthops"] + ] + ) + for mnh in found_hops: + for each_nh_in_multipath in mnh: + list2.append(each_nh_in_multipath) + if found_hops[0]: + missing_list_of_nexthops = set(list2).difference( + list1 + ) + additional_nexthops_in_required_nhs = set( + list1 + ).difference(list2) - if list2: - if additional_nexthops_in_required_nhs: - logger.info( - "Missing nexthop %s for route" - " %s in RIB of router %s\n", - additional_nexthops_in_required_nhs, - st_rt, - dut, - ) - errormsg = ( - "Nexthop {} is Missing for " - "route {} in RIB of router {}\n".format( + if list2: + if additional_nexthops_in_required_nhs: + logger.info( + "Missing nexthop %s for route" + " %s in RIB of router %s\n", additional_nexthops_in_required_nhs, st_rt, dut, ) - ) return errormsg else: nh_found = True + + elif next_hop and multi_nh is None: + if not isinstance(next_hop, list): + next_hop = [next_hop] + list1 = next_hop + found_hops = [rib_r["ip"] for rib_r in + rib_routes_json["routes"][ + st_rt][0]["nexthops"]] + list2 = found_hops + missing_list_of_nexthops = \ + set(list2).difference(list1) + additional_nexthops_in_required_nhs = \ + set(list1).difference(list2) + + if list2: + if additional_nexthops_in_required_nhs: + logger.info("Missing nexthop %s for route"\ + " %s in RIB of router %s\n", \ + additional_nexthops_in_required_nhs, \ + st_rt, dut) + errormsg=("Nexthop {} is Missing for "\ + "route {} in RIB of router {}\n".format( + additional_nexthops_in_required_nhs, + st_rt, dut)) + return errormsg + else: + nh_found = True + if aspath: found_paths = rib_routes_json["routes"][st_rt][0][ "path" @@ -3676,7 +3708,6 @@ def verify_attributes_for_evpn_routes( """ API to verify rd and rt value using "sh bgp l2vpn evpn 10.1.1.1" command. - Parameters ---------- * `tgen`: topogen object @@ -3690,7 +3721,6 @@ def verify_attributes_for_evpn_routes( * `ipLen` : IP prefix length * `rd_peer` : Peer name from which RD will be auto-generated * `rt_peer` : Peer name from which RT will be auto-generated - Usage ----- input_dict_1 = { @@ -4067,7 +4097,6 @@ def verify_evpn_routes( """ API to verify evpn routes using "sh bgp l2vpn evpn" command. - Parameters ---------- * `tgen`: topogen object @@ -4078,7 +4107,6 @@ def verify_evpn_routes( * `route_type` : Route type 5 is supported as of now * `EthTag` : Ethernet tag, by-default is 0 * `next_hop` : Prefered nexthop for the evpn routes - Usage ----- input_dict_1 = { @@ -4091,7 +4119,6 @@ def verify_evpn_routes( } } result = verify_evpn_routes(tgen, topo, input_dict) - Returns ------- errormsg(str) or True diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 6c24b6ddbb..3f360ef40a 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -31,7 +31,6 @@ from re import search as re_search from tempfile import mkdtemp import os -import io import sys import traceback import socket @@ -39,6 +38,7 @@ import ipaddress import platform if sys.version_info[0] > 2: + import io import configparser else: import StringIO @@ -151,8 +151,8 @@ class InvalidCLIError(Exception): def run_frr_cmd(rnode, cmd, isjson=False): """ - Execute frr show commands in priviledged mode - * `rnode`: router node on which commands needs to executed + Execute frr show commands in privileged mode + * `rnode`: router node on which command needs to be executed * `cmd`: Command to be executed on frr * `isjson`: If command is to get json data or not :return str: @@ -184,11 +184,11 @@ def apply_raw_config(tgen, input_dict): """ API to configure raw configuration on device. This can be used for any cli - which does has not been implemented in JSON. + which has not been implemented in JSON. Parameters ---------- - * `tgen`: tgen onject + * `tgen`: tgen object * `input_dict`: configuration that needs to be applied Usage @@ -232,8 +232,8 @@ def create_common_configuration( frr_json.conf and load to router Parameters ---------- - * `tgen`: tgen onject - * `data`: Congiguration data saved in a list. + * `tgen`: tgen object + * `data`: Configuration data saved in a list. * `router` : router id to be configured. * `config_type` : Syntactic information while writing configuration. Should be one of the value as mentioned in the config_map below. @@ -257,6 +257,8 @@ def create_common_configuration( "bgp": "! BGP Config\n", "vrf": "! VRF Config\n", "ospf": "! OSPF Config\n", + "ospf6": "! OSPF Config\n", + "pim": "! PIM Config\n", } ) @@ -292,8 +294,8 @@ def create_common_configuration( def kill_router_daemons(tgen, router, daemons): """ - Router's current config would be saved to /etc/frr/ for each deamon - and deamon would be killed forcefully using SIGKILL. + Router's current config would be saved to /etc/frr/ for each daemon + and daemon would be killed forcefully using SIGKILL. * `tgen` : topogen object * `router`: Device under test * `daemons`: list of daemons to be killed @@ -389,6 +391,8 @@ def check_router_status(tgen): daemons.append("bgpd") if "zebra" in result: daemons.append("zebra") + if "pimd" in result: + daemons.append("pimd") rnode.startDaemons(daemons) @@ -593,13 +597,13 @@ def load_config_to_router(tgen, routerName, save_bkup=False): def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None): """ - API to get the link local ipv6 address of a perticular interface using + API to get the link local ipv6 address of a particular interface using FRR command 'show interface' - * `tgen`: tgen onject - * `router` : router for which hightest interface should be + * `tgen`: tgen object + * `router` : router for which highest interface should be calculated - * `intf` : interface for which linklocal address needs to be taken + * `intf` : interface for which link-local address needs to be taken * `vrf` : VRF name Usage @@ -688,7 +692,7 @@ def generate_support_bundle(): def start_topology(tgen, daemon=None): """ Starting topology, create tmp files which are loaded to routers - to start deamons and then start routers + to start daemons and then start routers * `tgen` : topogen object """ @@ -696,7 +700,7 @@ def start_topology(tgen, daemon=None): # Starting topology tgen.start_topology() - # Starting deamons + # Starting daemons router_list = tgen.routers() ROUTER_LIST = sorted( @@ -735,28 +739,41 @@ def start_topology(tgen, daemon=None): except IOError as err: logger.error("I/O error({0}): {1}".format(err.errno, err.strerror)) - # Loading empty zebra.conf file to router, to start the zebra deamon + # Loading empty zebra.conf file to router, to start the zebra daemon router.load_config( TopoRouter.RD_ZEBRA, "{}/{}/zebra.conf".format(TMPDIR, rname) ) - # Loading empty bgpd.conf file to router, to start the bgp deamon + # Loading empty bgpd.conf file to router, to start the bgp daemon router.load_config(TopoRouter.RD_BGP, "{}/{}/bgpd.conf".format(TMPDIR, rname)) if daemon and "ospfd" in daemon: - # Loading empty ospf.conf file to router, to start the bgp deamon + # Loading empty ospf.conf file to router, to start the bgp daemon router.load_config( TopoRouter.RD_OSPF, "{}/{}/ospfd.conf".format(TMPDIR, rname) ) - # Starting routers + + if daemon and "ospf6d" in daemon: + # Loading empty ospf.conf file to router, to start the bgp daemon + router.load_config( + TopoRouter.RD_OSPF6, "{}/{}/ospf6d.conf".format(TMPDIR, rname) + ) + + if daemon and "pimd" in daemon: + # Loading empty pimd.conf file to router, to start the pim deamon + router.load_config( + TopoRouter.RD_PIM, "{}/{}/pimd.conf".format(TMPDIR, rname) + ) + + # Starting routers logger.info("Starting all routers once topology is created") tgen.start_router() def stop_router(tgen, router): """ - Router"s current config would be saved to /etc/frr/ for each deamon - and router and its deamons would be stopped. + Router"s current config would be saved to /tmp/topotest/<suite>/<router> for each daemon + and router and its daemons would be stopped. * `tgen` : topogen object * `router`: Device under test @@ -774,8 +791,8 @@ def stop_router(tgen, router): def start_router(tgen, router): """ - Router will started and config would be loaded from /etc/frr/ for each - deamon + Router will be started and config would be loaded from /tmp/topotest/<suite>/<router> for each + daemon * `tgen` : topogen object * `router`: Device under test @@ -786,8 +803,8 @@ def start_router(tgen, router): try: router_list = tgen.routers() - # Router and its deamons would be started and config would - # be loaded to router for each deamon from /etc/frr + # Router and its daemons would be started and config would + # be loaded to router for each daemon from /etc/frr router_list[router].start() # Waiting for router to come up @@ -835,9 +852,204 @@ def topo_daemons(tgen, topo): if "ospf" in topo["routers"][rtr] and "ospfd" not in daemon_list: daemon_list.append("ospfd") + if "ospf6" in topo["routers"][rtr] and "ospf6d" not in daemon_list: + daemon_list.append("ospf6d") + + for val in topo["routers"][rtr]["links"].values(): + if "pim" in val and "pimd" not in daemon_list: + daemon_list.append("pimd") + break + return daemon_list +def add_interfaces_to_vlan(tgen, input_dict): + """ + Add interfaces to VLAN, we need vlan pakcage to be installed on machine + + * `tgen`: tgen onject + * `input_dict` : interfaces to be added to vlans + + input_dict= { + "r1":{ + "vlan":{ + VLAN_1: [{ + intf_r1_s1: { + "ip": "10.1.1.1", + "subnet": "255.255.255.0 + } + }] + } + } + } + + add_interfaces_to_vlan(tgen, input_dict) + + """ + + router_list = tgen.routers() + for dut in input_dict.keys(): + rnode = tgen.routers()[dut] + + if "vlan" in input_dict[dut]: + for vlan, interfaces in input_dict[dut]["vlan"].items(): + for intf_dict in interfaces: + for interface, data in intf_dict.items(): + # Adding interface to VLAN + cmd = "vconfig add {} {}".format(interface, vlan) + logger.info("[DUT: %s]: Running command: %s", dut, cmd) + rnode.run(cmd) + + vlan_intf = "{}.{}".format(interface, vlan) + + ip = data["ip"] + subnet = data["subnet"] + + # Bringing interface up + cmd = "ip link set up {}".format(vlan_intf) + logger.info("[DUT: %s]: Running command: %s", dut, cmd) + rnode.run(cmd) + + # Assigning IP address + cmd = "ifconfig {} {} netmask {}".format(vlan_intf, ip, subnet) + logger.info("[DUT: %s]: Running command: %s", dut, cmd) + rnode.run(cmd) + + +def tcpdump_capture_start( + tgen, + router, + intf, + protocol=None, + grepstr=None, + timeout=0, + options=None, + cap_file=None, + background=True, +): + """ + API to capture network packets using tcp dump. + + Packages used : + + Parameters + ---------- + * `tgen`: topogen object. + * `router`: router on which ping has to be performed. + * `intf` : interface for capture. + * `protocol` : protocol for which packet needs to be captured. + * `grepstr` : string to filter out tcp dump output. + * `timeout` : Time for which packet needs to be captured. + * `options` : options for TCP dump, all tcpdump options can be used. + * `cap_file` : filename to store capture dump. + * `background` : Make tcp dump run in back ground. + + Usage + ----- + tcpdump_result = tcpdump_dut(tgen, 'r2', intf, protocol='tcp', timeout=20, + options='-A -vv -x > r2bgp.txt ') + Returns + ------- + 1) True for successful capture + 2) errormsg - when tcp dump fails + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + rnode = tgen.routers()[router] + + if timeout > 0: + cmd = "timeout {}".format(timeout) + else: + cmd = "" + + cmdargs = "{} tcpdump".format(cmd) + + if intf: + cmdargs += " -i {}".format(str(intf)) + if protocol: + cmdargs += " {}".format(str(protocol)) + if options: + cmdargs += " -s 0 {}".format(str(options)) + + if cap_file: + file_name = os.path.join(LOGDIR, tgen.modname, router, cap_file) + cmdargs += " -w {}".format(str(file_name)) + # Remove existing capture file + rnode.run("rm -rf {}".format(file_name)) + + if grepstr: + cmdargs += ' | grep "{}"'.format(str(grepstr)) + + logger.info("Running tcpdump command: [%s]", cmdargs) + if not background: + rnode.run(cmdargs) + else: + rnode.run("nohup {} & /dev/null 2>&1".format(cmdargs)) + + # Check if tcpdump process is running + if background: + result = rnode.run("pgrep tcpdump") + logger.debug("ps -ef | grep tcpdump \n {}".format(result)) + + if not result: + errormsg = "tcpdump is not running {}".format("tcpdump") + return errormsg + else: + logger.info("Packet capture started on %s: interface %s", router, intf) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def tcpdump_capture_stop(tgen, router): + """ + API to capture network packets using tcp dump. + + Packages used : + + Parameters + ---------- + * `tgen`: topogen object. + * `router`: router on which ping has to be performed. + * `intf` : interface for capture. + * `protocol` : protocol for which packet needs to be captured. + * `grepstr` : string to filter out tcp dump output. + * `timeout` : Time for which packet needs to be captured. + * `options` : options for TCP dump, all tcpdump options can be used. + * `cap2file` : filename to store capture dump. + * `bakgrnd` : Make tcp dump run in back ground. + + Usage + ----- + tcpdump_result = tcpdump_dut(tgen, 'r2', intf, protocol='tcp', timeout=20, + options='-A -vv -x > r2bgp.txt ') + Returns + ------- + 1) True for successful capture + 2) errormsg - when tcp dump fails + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + rnode = tgen.routers()[router] + + # Check if tcpdump process is running + result = rnode.run("ps -ef | grep tcpdump") + logger.debug("ps -ef | grep tcpdump \n {}".format(result)) + + if not re_search(r"{}".format("tcpdump"), result): + errormsg = "tcpdump is not running {}".format("tcpdump") + return errormsg + else: + ppid = tgen.net.nameToNode[rnode.name].pid + rnode.run("set +m; pkill -P %s tcpdump &> /dev/null" % ppid) + logger.info("Stopped tcpdump capture") + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + ############################################# # Common APIs, will be used by all protocols ############################################# @@ -1137,11 +1349,11 @@ def generate_ips(network, no_of_ips): """ Returns list of IPs. based on start_ip and no_of_ips + * `network` : from here the ip will start generating, start_ip will be * `no_of_ips` : these many IPs will be generated """ - ipaddress_list = [] if type(network) is not list: network = [network] @@ -1152,14 +1364,20 @@ def generate_ips(network, no_of_ips): mask = int(start_ipaddr.split("/")[1]) else: logger.debug("start_ipaddr {} must have a / in it".format(start_ipaddr)) - assert(0) + assert 0 addr_type = validate_ip_address(start_ip) if addr_type == "ipv4": - start_ip = ipaddress.IPv4Address(frr_unicode(start_ip)) + if start_ip == "0.0.0.0" and mask == 0 and no_of_ips == 1: + ipaddress_list.append("{}/{}".format(start_ip, mask)) + return ipaddress_list + start_ip = ipaddress.IPv4Address(unicode(start_ip)) step = 2 ** (32 - mask) if addr_type == "ipv6": - start_ip = ipaddress.IPv6Address(frr_unicode(start_ip)) + if start_ip == "0::0" and mask == 0 and no_of_ips == 1: + ipaddress_list.append("{}/{}".format(start_ip, mask)) + return ipaddress_list + start_ip = ipaddress.IPv6Address(unicode(start_ip)) step = 2 ** (128 - mask) next_ip = start_ip @@ -1181,7 +1399,7 @@ def find_interface_with_greater_ip(topo, router, loopback=True, interface=True): it will return highest IP from loopback IPs otherwise from physical interface IPs. * `topo` : json file data - * `router` : router for which hightest interface should be calculated + * `router` : router for which highest interface should be calculated """ link_data = topo["routers"][router]["links"] @@ -1287,7 +1505,6 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict _wait = kwargs.pop("wait", wait) _attempts = kwargs.pop("attempts", attempts) _attempts = int(_attempts) - expected = True if _attempts < 0: raise ValueError("attempts must be 0 or greater") @@ -1297,12 +1514,10 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict _return_is_str = kwargs.pop("return_is_str", return_is_str) _return_is_dict = kwargs.pop("return_is_str", return_is_dict) + _expected = kwargs.setdefault("expected", True) + kwargs.pop("expected") for i in range(1, _attempts + 1): try: - _expected = kwargs.setdefault("expected", True) - if _expected is False: - expected = _expected - kwargs.pop("expected") ret = func(*args, **kwargs) logger.debug("Function returned %s", ret) if _return_is_str and isinstance(ret, bool) and _expected: @@ -1314,11 +1529,11 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict if _return_is_dict and isinstance(ret, dict): return ret - if _attempts == i and expected: + if _attempts == i: generate_support_bundle() return ret except Exception as err: - if _attempts == i and expected: + if _attempts == i: generate_support_bundle() logger.info("Max number of attempts (%r) reached", _attempts) raise @@ -1360,6 +1575,17 @@ def step(msg, reset=False): _step(msg, reset) +def do_countdown(secs): + """ + Countdown timer display + """ + for i in range(secs, 0, -1): + sys.stdout.write("{} ".format(str(i))) + sys.stdout.flush() + sleep(1) + return + + ############################################# # These APIs, will used by testcase ############################################# @@ -1378,6 +1604,25 @@ def create_interfaces_cfg(tgen, topo, build=False): ------- True or False """ + + def _create_interfaces_ospf_cfg(ospf, c_data, data, ospf_keywords): + interface_data = [] + ip_ospf = "ipv6 ospf6" if ospf == "ospf6" else "ip ospf" + for keyword in ospf_keywords: + if keyword in data[ospf]: + intf_ospf_value = c_data["links"][destRouterLink][ospf][keyword] + if "delete" in data and data["delete"]: + interface_data.append( + "no {} {}".format(ip_ospf, keyword.replace("_", "-")) + ) + else: + interface_data.append( + "{} {} {}".format( + ip_ospf, keyword.replace("_", "-"), intf_ospf_value + ) + ) + return interface_data + result = False topo = deepcopy(topo) @@ -1424,66 +1669,26 @@ def create_interfaces_cfg(tgen, topo, build=False): else: interface_data.append("ipv6 address {}\n".format(intf_addr)) + ospf_keywords = [ + "hello_interval", + "dead_interval", + "network", + "priority", + "cost", + ] if "ospf" in data: - ospf_data = data["ospf"] - if "area" in ospf_data: - intf_ospf_area = c_data["links"][destRouterLink]["ospf"]["area"] - if "delete" in data and data["delete"]: - interface_data.append("no ip ospf area") - else: - interface_data.append( - "ip ospf area {}".format(intf_ospf_area) - ) - - if "hello_interval" in ospf_data: - intf_ospf_hello = c_data["links"][destRouterLink]["ospf"][ - "hello_interval" - ] - if "delete" in data and data["delete"]: - interface_data.append("no ip ospf " " hello-interval") - else: - interface_data.append( - "ip ospf " " hello-interval {}".format(intf_ospf_hello) - ) - - if "dead_interval" in ospf_data: - intf_ospf_dead = c_data["links"][destRouterLink]["ospf"][ - "dead_interval" - ] - if "delete" in data and data["delete"]: - interface_data.append("no ip ospf" " dead-interval") - else: - interface_data.append( - "ip ospf " " dead-interval {}".format(intf_ospf_dead) - ) - - if "network" in ospf_data: - intf_ospf_nw = c_data["links"][destRouterLink]["ospf"][ - "network" - ] - if "delete" in data and data["delete"]: - interface_data.append( - "no ip ospf" " network {}".format(intf_ospf_nw) - ) - else: - interface_data.append( - "ip ospf" " network {}".format(intf_ospf_nw) - ) - - if "priority" in ospf_data: - intf_ospf_nw = c_data["links"][destRouterLink]["ospf"][ - "priority" - ] + interface_data += _create_interfaces_ospf_cfg( + "ospf", c_data, data, ospf_keywords + ["area"] + ) + if "ospf6" in data: + interface_data += _create_interfaces_ospf_cfg( + "ospf6", c_data, data, ospf_keywords + ) - if "delete" in data and data["delete"]: - interface_data.append("no ip ospf" " priority") - else: - interface_data.append( - "ip ospf" " priority {}".format(intf_ospf_nw) - ) result = create_common_configuration( tgen, c_router, interface_data, "interface_config", build=build ) + except InvalidCLIError: # Traceback errormsg = traceback.format_exc() @@ -2222,9 +2427,9 @@ def shutdown_bringup_interface(tgen, dut, intf_name, ifaceaction=False): ----- dut = "r3" intf = "r3-r1-eth0" - # Shut down ineterface + # Shut down interface shutdown_bringup_interface(tgen, dut, intf, False) - # Bring up ineterface + # Bring up interface shutdown_bringup_interface(tgen, dut, intf, True) Returns ------- @@ -2233,13 +2438,58 @@ def shutdown_bringup_interface(tgen, dut, intf_name, ifaceaction=False): router_list = tgen.routers() if ifaceaction: - logger.info("Bringing up interface : {}".format(intf_name)) + logger.info("Bringing up interface {} : {}".format(dut, intf_name)) else: - logger.info("Shutting down interface : {}".format(intf_name)) + logger.info("Shutting down interface {} : {}".format(dut, intf_name)) interface_set_status(router_list[dut], intf_name, ifaceaction) +def stop_router(tgen, router): + """ + Router's current config would be saved to /tmp/topotest/<suite>/<router> + for each daemon and router and its daemons would be stopped. + + * `tgen` : topogen object + * `router`: Device under test + """ + + router_list = tgen.routers() + + # Saving router config to /etc/frr, which will be loaded to router + # when it starts + router_list[router].vtysh_cmd("write memory") + + # Stop router + router_list[router].stop() + + +def start_router(tgen, router): + """ + Router will be started and config would be loaded from + /tmp/topotest/<suite>/<router> for each daemon + + * `tgen` : topogen object + * `router`: Device under test + """ + + logger.debug("Entering lib API: start_router") + + try: + router_list = tgen.routers() + + # Router and its daemons would be started and config would + # be loaded to router for each daemon from /etc/frr + router_list[router].start() + + except Exception as e: + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: start_router()") + + def addKernelRoute( tgen, router, intf, group_addr_range, next_hop=None, src=None, del_action=None ): @@ -2250,7 +2500,7 @@ def addKernelRoute( ----------- * `tgen` : Topogen object * `router`: router for which kernal routes needs to be added - * `intf`: interface name, for which kernal routes needs to be added + * `intf`: interface name, for which kernel routes needs to be added * `bindToAddress`: bind to <host>, an interface or multicast address @@ -2313,7 +2563,7 @@ def configure_vxlan(tgen, input_dict): """ Add and configure vxlan - * `tgen`: tgen onject + * `tgen`: tgen object * `input_dict` : data for vxlan config Usage: @@ -2414,7 +2664,7 @@ def configure_brctl(tgen, topo, input_dict): """ Add and configure brctl - * `tgen`: tgen onject + * `tgen`: tgen object * `input_dict` : data for brctl config Usage: @@ -2508,7 +2758,7 @@ def configure_interface_mac(tgen, input_dict): """ Add and configure brctl - * `tgen`: tgen onject + * `tgen`: tgen object * `input_dict` : data for mac config input_mac= { @@ -2562,7 +2812,7 @@ def verify_rib( tag=None, metric=None, fib=None, - count_only=False + count_only=False, ): """ Data will be read from input_dict or input JSON file, API will generate @@ -2754,8 +3004,10 @@ def verify_rib( "Nexthops are missing for " "route {} in RIB of router {}: " "expected {}, found {}\n".format( - st_rt, dut, len(next_hop), - len(found_hops) + st_rt, + dut, + len(next_hop), + len(found_hops), ) ) return errormsg @@ -2802,7 +3054,11 @@ def verify_rib( errormsg = ( "[DUT: {}]: tag value {}" " is not matched for" - " route {} in RIB \n".format(dut, _tag, st_rt,) + " route {} in RIB \n".format( + dut, + _tag, + st_rt, + ) ) return errormsg @@ -2819,7 +3075,11 @@ def verify_rib( errormsg = ( "[DUT: {}]: metric value " "{} is not matched for " - "route {} in RIB \n".format(dut, metric, st_rt,) + "route {} in RIB \n".format( + dut, + metric, + st_rt, + ) ) return errormsg @@ -2868,7 +3128,9 @@ def verify_rib( for advertise_network_dict in advertise_network: if "vrf" in advertise_network_dict: - cmd = "{} vrf {} json".format(command, advertise_network_dict["vrf"]) + cmd = "{} vrf {} json".format( + command, advertise_network_dict["vrf"] + ) else: cmd = "{} json".format(command) @@ -2947,6 +3209,7 @@ def verify_rib( logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True + @retry(attempts=6, wait=2, return_is_str=True) def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): """ @@ -3327,7 +3590,12 @@ def verify_prefix_lists(tgen, input_dict): for addr_type in prefix_lists_addr: if not check_address_types(addr_type): continue - + # show ip prefix list + if addr_type == "ipv4": + cmd = "show ip prefix-list" + else: + cmd = "show {} prefix-list".format(addr_type) + show_prefix_list = run_frr_cmd(rnode, cmd) for prefix_list in prefix_lists_addr[addr_type].keys(): if prefix_list in show_prefix_list: errormsg = ( @@ -3550,7 +3818,6 @@ def verify_cli_json(tgen, input_dict): """ API to verify if JSON is available for clis command. - Parameters ---------- * `tgen`: topogen object @@ -3720,7 +3987,6 @@ def verify_vrf_vni(tgen, input_dict): """ API to verify vrf vni details using "show vrf vni json" command. - Parameters ---------- * `tgen`: topogen object @@ -3852,3 +4118,269 @@ def required_linux_kernel_version(required_version): ) return error_msg return True + + +def iperfSendIGMPJoin( + tgen, server, bindToAddress, l4Type="UDP", join_interval=1, inc_step=0, repeat=0 +): + """ + Run iperf to send IGMP join and traffic + + Parameters: + ----------- + * `tgen` : Topogen object + * `l4Type`: string, one of [ TCP, UDP ] + * `server`: iperf server, from where IGMP join would be sent + * `bindToAddress`: bind to <host>, an interface or multicast + address + * `join_interval`: seconds between periodic bandwidth reports + * `inc_step`: increamental steps, by default 0 + * `repeat`: Repetition of group, by default 0 + + returns: + -------- + errormsg or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + rnode = tgen.routers()[server] + + iperfArgs = "iperf -s " + + # UDP/TCP + if l4Type == "UDP": + iperfArgs += "-u " + + iperfCmd = iperfArgs + # Group address range to cover + if bindToAddress: + if type(bindToAddress) is not list: + Address = [] + start = ipaddress.IPv4Address(frr_unicode(bindToAddress)) + + Address = [start] + next_ip = start + + count = 1 + while count < repeat: + next_ip += inc_step + Address.append(next_ip) + count += 1 + bindToAddress = Address + + for bindTo in bindToAddress: + iperfArgs = iperfCmd + iperfArgs += "-B %s " % bindTo + + # Join interval + if join_interval: + iperfArgs += "-i %d " % join_interval + + iperfArgs += " &>/dev/null &" + # Run iperf command to send IGMP join + logger.debug("[DUT: {}]: Running command: [{}]".format(server, iperfArgs)) + output = rnode.run("set +m; {} sleep 0.5".format(iperfArgs)) + + # Check if iperf process is running + if output: + pid = output.split()[1] + rnode.run("touch /var/run/frr/iperf_server.pid") + rnode.run("echo %s >> /var/run/frr/iperf_server.pid" % pid) + else: + errormsg = "IGMP join is not sent for {}. Error: {}".format(bindTo, output) + logger.error(output) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def iperfSendTraffic( + tgen, + client, + bindToAddress, + ttl, + time=0, + l4Type="UDP", + inc_step=0, + repeat=0, + mappedAddress=None, +): + """ + Run iperf to send IGMP join and traffic + + Parameters: + ----------- + * `tgen` : Topogen object + * `l4Type`: string, one of [ TCP, UDP ] + * `client`: iperf client, from where iperf traffic would be sent + * `bindToAddress`: bind to <host>, an interface or multicast + address + * `ttl`: time to live + * `time`: time in seconds to transmit for + * `inc_step`: increamental steps, by default 0 + * `repeat`: Repetition of group, by default 0 + * `mappedAddress`: Mapped Interface ip address + + returns: + -------- + errormsg or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + rnode = tgen.routers()[client] + + iperfArgs = "iperf -c " + + iperfCmd = iperfArgs + # Group address range to cover + if bindToAddress: + if type(bindToAddress) is not list: + Address = [] + start = ipaddress.IPv4Address(frr_unicode(bindToAddress)) + + Address = [start] + next_ip = start + + count = 1 + while count < repeat: + next_ip += inc_step + Address.append(next_ip) + count += 1 + bindToAddress = Address + + for bindTo in bindToAddress: + iperfArgs = iperfCmd + iperfArgs += "%s " % bindTo + + # Mapped Interface IP + if mappedAddress: + iperfArgs += "-B %s " % mappedAddress + + # UDP/TCP + if l4Type == "UDP": + iperfArgs += "-u -b 0.012m " + + # TTL + if ttl: + iperfArgs += "-T %d " % ttl + + # Time + if time: + iperfArgs += "-t %d " % time + + iperfArgs += " &>/dev/null &" + + # Run iperf command to send multicast traffic + logger.debug("[DUT: {}]: Running command: [{}]".format(client, iperfArgs)) + output = rnode.run("set +m; {} sleep 0.5".format(iperfArgs)) + + # Check if iperf process is running + if output: + pid = output.split()[1] + rnode.run("touch /var/run/frr/iperf_client.pid") + rnode.run("echo %s >> /var/run/frr/iperf_client.pid" % pid) + else: + errormsg = "Multicast traffic is not sent for {}. Error {}".format( + bindTo, output + ) + logger.error(output) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def kill_iperf(tgen, dut=None, action=None): + """ + Killing iperf process if running for any router in topology + Parameters: + ----------- + * `tgen` : Topogen object + * `dut` : Any iperf hostname to send igmp prune + * `action`: to kill igmp join iperf action is remove_join + to kill traffic iperf action is remove_traffic + + Usage + ---- + kill_iperf(tgen, dut ="i6", action="remove_join") + + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + router_list = tgen.routers() + for router, rnode in router_list.items(): + # Run iperf command to send IGMP join + pid_client = rnode.run("cat /var/run/frr/iperf_client.pid") + pid_server = rnode.run("cat /var/run/frr/iperf_server.pid") + if action == "remove_join": + pids = pid_server + elif action == "remove_traffic": + pids = pid_client + else: + pids = "\n".join([pid_client, pid_server]) + for pid in pids.split("\n"): + pid = pid.strip() + if pid.isdigit(): + cmd = "set +m; kill -9 %s &> /dev/null" % pid + logger.debug("[DUT: {}]: Running command: [{}]".format(router, cmd)) + rnode.run(cmd) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + +def verify_ip_nht(tgen, input_dict): + """ + Running "show ip nht" command and verifying given nexthop resolution + Parameters + ---------- + * `tgen` : topogen object + * `input_dict`: data to verify nexthop + Usage + ----- + input_dict_4 = { + "r1": { + nh: { + "Address": nh, + "resolvedVia": "connected", + "nexthops": { + "nexthop1": { + "Interface": intf + } + } + } + } + } + result = verify_ip_nht(tgen, input_dict_4) + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: verify_ip_nht()") + + for router in input_dict.keys(): + if router not in tgen.routers(): + continue + + rnode = tgen.routers()[router] + nh_list = input_dict[router] + + if validate_ip_address(nh_list.keys()[0]) is "ipv6": + show_ip_nht = run_frr_cmd(rnode, "show ipv6 nht") + else: + show_ip_nht = run_frr_cmd(rnode, "show ip nht") + + for nh in nh_list: + if nh in show_ip_nht: + logger.info("Nexthop %s is resolved on %s", nh, router) + return True + else: + errormsg = "Nexthop {} is resolved on {}".format(nh, router) + return errormsg + + logger.debug("Exiting lib API: verify_ip_nht()") + return False diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py index 9cbea67af1..0b6a946fda 100644 --- a/tests/topotests/lib/lutil.py +++ b/tests/topotests/lib/lutil.py @@ -23,6 +23,7 @@ import time import datetime import json import math +import time from lib.topolog import logger from mininet.net import Mininet @@ -194,8 +195,9 @@ Total %-4d %-4d %d\n\ if op != "wait": self.l_line += 1 self.log( - "(#%d) %s:%s COMMAND:%s:%s:%s:%s:%s:" + "%s (#%d) %s:%s COMMAND:%s:%s:%s:%s:%s:" % ( + time.asctime(), self.l_total + 1, self.l_filename, self.l_line, diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index 9f3d4841b0..5bc9f14fea 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -62,7 +62,7 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru "r1": { "ospf": { "router_id": "22.22.22.22", - "area": [{ "id":0.0.0.0, "type": "nssa"}] + "area": [{ "id": "0.0.0.0", "type": "nssa"}] } } @@ -94,7 +94,7 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru return result -def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True): +def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True, ospf="ospf"): """ Helper API to create ospf global configuration. @@ -105,6 +105,33 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True * `router` : router to be configured. * `build` : Only for initial setup phase this is set as True. * `load_config` : Loading the config to router this is set as True. + * `ospf` : either 'ospf' or 'ospf6' + + Usage + ----- + input_dict = { + "routers": { + "r1": { + "links": { + "r3": { + "ipv6": "2013:13::1/64", + "ospf6": { + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf6": { + "router_id": "1.1.1.1", + "neighbors": { + "r3": { + "area": "1.1.1.1" + } + } + } + } + } Returns ------- @@ -115,17 +142,17 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True logger.debug("Entering lib API: __create_ospf_global()") try: - ospf_data = input_dict[router]["ospf"] + ospf_data = input_dict[router][ospf] del_ospf_action = ospf_data.setdefault("delete", False) if del_ospf_action: - config_data = ["no router ospf"] + config_data = ["no router {}".format(ospf)] result = create_common_configuration( - tgen, router, config_data, "ospf", build, load_config + tgen, router, config_data, ospf, build, load_config ) return result config_data = [] - cmd = "router ospf" + cmd = "router {}".format(ospf) config_data.append(cmd) @@ -133,9 +160,9 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True router_id = ospf_data.setdefault("router_id", None) del_router_id = ospf_data.setdefault("del_router_id", False) if del_router_id: - config_data.append("no ospf router-id") + config_data.append("no {} router-id".format(ospf)) if router_id: - config_data.append("ospf router-id {}".format(router_id)) + config_data.append("{} router-id {}".format(ospf, router_id)) # redistribute command redistribute_data = ospf_data.setdefault("redistribute", {}) @@ -154,6 +181,7 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True if del_action: cmd = "no {}".format(cmd) config_data.append(cmd) + # area information area_data = ospf_data.setdefault("area", {}) if area_data: @@ -172,9 +200,20 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True if del_action: cmd = "no {}".format(cmd) config_data.append(cmd) - result = create_common_configuration( - tgen, router, config_data, "ospf", build, load_config - ) + + # area interface information for ospf6d only + if ospf == "ospf6": + area_iface = ospf_data.setdefault("neighbors", {}) + if area_iface: + for neighbor in area_iface: + if "area" in area_iface[neighbor]: + iface = input_dict[router]["links"][neighbor]["interface"] + cmd = "interface {} area {}".format( + iface, area_iface[neighbor]["area"] + ) + if area_iface[neighbor].setdefault("delete", False): + cmd = "no {}".format(cmd) + config_data.append(cmd) # summary information summary_data = ospf_data.setdefault("summary-address", {}) @@ -200,8 +239,9 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True if del_action: cmd = "no {}".format(cmd) config_data.append(cmd) + result = create_common_configuration( - tgen, router, config_data, "ospf", build, load_config + tgen, router, config_data, ospf, build, load_config ) except InvalidCLIError: @@ -238,7 +278,7 @@ def create_router_ospf6(tgen, topo, input_dict=None, build=False, load_config=Tr ------- True or False """ - logger.debug("Entering lib API: create_router_ospf()") + logger.debug("Entering lib API: create_router_ospf6()") result = False if not input_dict: @@ -247,67 +287,15 @@ def create_router_ospf6(tgen, topo, input_dict=None, build=False, load_config=Tr topo = topo["routers"] input_dict = deepcopy(input_dict) for router in input_dict.keys(): - if "ospf" not in input_dict[router]: - logger.debug("Router %s: 'ospf' not present in input_dict", router) + if "ospf6" not in input_dict[router]: + logger.debug("Router %s: 'ospf6' not present in input_dict", router) continue - result = __create_ospf_global(tgen, input_dict, router, build, load_config) - - logger.debug("Exiting lib API: create_router_ospf()") - return result - - -def __create_ospf6_global(tgen, input_dict, router, build=False, load_config=True): - """ - Helper API to create ospf global configuration. - - Parameters - ---------- - * `tgen` : Topogen object - * `input_dict` : Input dict data, required when configuring from testcase - * `router` : router id to be configured. - * `build` : Only for initial setup phase this is set as True. - - Returns - ------- - True or False - """ - - result = False - logger.debug("Entering lib API: __create_ospf_global()") - try: - - ospf_data = input_dict[router]["ospf6"] - del_ospf_action = ospf_data.setdefault("delete", False) - if del_ospf_action: - config_data = ["no ipv6 router ospf"] - result = create_common_configuration( - tgen, router, config_data, "ospf", build, load_config - ) - return result - - config_data = [] - cmd = "router ospf" - - config_data.append(cmd) - - router_id = ospf_data.setdefault("router_id", None) - del_router_id = ospf_data.setdefault("del_router_id", False) - if del_router_id: - config_data.append("no ospf router-id") - if router_id: - config_data.append("ospf router-id {}".format(router_id)) - - result = create_common_configuration( - tgen, router, config_data, "ospf", build, load_config + result = __create_ospf_global( + tgen, input_dict, router, build, load_config, "ospf6" ) - except InvalidCLIError: - # Traceback - errormsg = traceback.format_exc() - logger.error(errormsg) - return errormsg - logger.debug("Exiting lib API: create_ospf_global()") + logger.debug("Exiting lib API: create_router_ospf6()") return result @@ -330,7 +318,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config= "links": { "r2": { "ospf": { - "authentication": 'message-digest', + "authentication": "message-digest", "authentication-key": "ospf", "message-digest-key": "10" } @@ -379,6 +367,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config= if data_ospf_area: cmd = "ip ospf area {}".format(data_ospf_area) config_data.append(cmd) + # interface ospf auth if data_ospf_auth: if data_ospf_auth == "null": @@ -464,6 +453,32 @@ def clear_ospf(tgen, router): logger.debug("Exiting lib API: clear_ospf()") +def redistribute_ospf(tgen, topo, dut, route_type, **kwargs): + """ + Redstribution of routes inside ospf. + + Parameters + ---------- + * `tgen`: Topogen object + * `topo` : json file data + * `dut`: device under test + * `route_type`: "static" or "connected" or .... + * `kwargs`: pass extra information (see below) + + Usage + ----- + redistribute_ospf(tgen, topo, "r0", "static", delete=True) + redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4") + """ + + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": route_type}]}}} + for k, v in kwargs.items(): + ospf_red[dut]["ospf"]["redistribute"][0][k] = v + + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ################################ # Verification procs ################################ @@ -525,7 +540,7 @@ def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False): logger.info("Verifying OSPF neighborship on router %s:", router) show_ospf_json = run_frr_cmd( - rnode, "show ip ospf neighbor all json", isjson=True + rnode, "show ip ospf neighbor all json", isjson=True ) # Verifying output dictionary show_ospf_json is empty or not @@ -658,6 +673,70 @@ def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False): return result +################################ +# Verification procs +################################ +@retry(attempts=40, wait=2, return_is_str=True) +def verify_ospf6_neighbor(tgen, topo): + """ + This API is to verify ospf neighborship by running + show ip ospf neighbour command, + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + + Usage + ----- + Check FULL neighbors. + verify_ospf_neighbor(tgen, topo) + + result = verify_ospf_neighbor(tgen, topo) + + Returns + ------- + True or False (Error Message) + """ + + logger.debug("Entering lib API: verify_ospf6_neighbor()") + result = False + for router, rnode in tgen.routers().items(): + if "ospf6" not in topo["routers"][router]: + continue + + logger.info("Verifying OSPF6 neighborship on router %s:", router) + show_ospf_json = run_frr_cmd( + rnode, "show ipv6 ospf6 neighbor json", isjson=True + ) + + if not show_ospf_json: + return "OSPF6 is not running" + + ospf_nbr_list = topo["routers"][router]["ospf6"]["neighbors"] + no_of_peer = 0 + for ospf_nbr in ospf_nbr_list: + ospf_nbr_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"] + for neighbor in show_ospf_json["neighbors"]: + if neighbor["neighborId"] == ospf_nbr_rid: + nh_state = neighbor["state"] + break + else: + return "[DUT: {}] OSPF6 peer {} missing".format(router, data_rid) + + if nh_state == "Full": + no_of_peer += 1 + + if no_of_peer == len(ospf_nbr_list): + logger.info("[DUT: {}] OSPF6 is Converged".format(router)) + result = True + else: + return "[DUT: {}] OSPF6 is not Converged".format(router) + + logger.debug("Exiting API: verify_ospf6_neighbor()") + return result + + @retry(attempts=21, wait=2, return_is_str=True) def verify_ospf_rib( tgen, dut, input_dict, next_hop=None, tag=None, metric=None, fib=None @@ -847,19 +926,23 @@ def verify_ospf_rib( if "routeType" not in ospf_rib_json[st_rt]: errormsg = ( "[DUT: {}]: routeType missing" - "for route {} in OSPF RIB \n".format(dut, st_rt) + " for route {} in OSPF RIB \n".format( + dut, st_rt + ) ) return errormsg elif _rtype != ospf_rib_json[st_rt]["routeType"]: errormsg = ( "[DUT: {}]: routeType mismatch" - "for route {} in OSPF RIB \n".format(dut, st_rt) + " for route {} in OSPF RIB \n".format( + dut, st_rt + ) ) return errormsg else: logger.info( - "DUT: {}]: Found routeType {}" - "for route {}".format(dut, _rtype, st_rt) + "[DUT: {}]: Found routeType {}" + " for route {}".format(dut, _rtype, st_rt) ) if tag: if "tag" not in ospf_rib_json[st_rt]: @@ -874,7 +957,11 @@ def verify_ospf_rib( errormsg = ( "[DUT: {}]: tag value {}" " is not matched for" - " route {} in RIB \n".format(dut, _tag, st_rt,) + " route {} in RIB \n".format( + dut, + _tag, + st_rt, + ) ) return errormsg @@ -891,7 +978,11 @@ def verify_ospf_rib( errormsg = ( "[DUT: {}]: metric value " "{} is not matched for " - "route {} in RIB \n".format(dut, metric, st_rt,) + "route {} in RIB \n".format( + dut, + metric, + st_rt, + ) ) return errormsg diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py new file mode 100644 index 0000000000..294f60bf68 --- /dev/null +++ b/tests/topotests/lib/pim.py @@ -0,0 +1,3450 @@ +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. + +import sys +import os +import re +import datetime +import traceback +import pytest +from time import sleep +from copy import deepcopy +from lib.topolog import logger + +# Import common_config to use commomnly used APIs +from lib.common_config import ( + create_common_configuration, + InvalidCLIError, + retry, + run_frr_cmd, +) + +#### +CWD = os.path.dirname(os.path.realpath(__file__)) + + +def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True): + """ + API to configure pim on router + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from + testcase + * `build` : Only for initial setup phase this is set as True. + + Usage + ----- + input_dict = { + "r1": { + "pim": { + "disable" : ["l1-i1-eth1"], + "rp": [{ + "rp_addr" : "1.0.3.17". + "keep-alive-timer": "100" + "group_addr_range": ["224.1.1.0/24", "225.1.1.0/24"] + "prefix-list": "pf_list_1" + "delete": True + }] + } + } + } + + + Returns + ------- + True or False + """ + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + result = False + if not input_dict: + input_dict = deepcopy(topo) + else: + topo = topo["routers"] + input_dict = deepcopy(input_dict) + for router in input_dict.keys(): + result = _enable_disable_pim(tgen, topo, input_dict, router, build) + + if "pim" not in input_dict[router]: + logger.debug("Router %s: 'pim' is not present in " "input_dict", router) + continue + + if result is True: + if "rp" not in input_dict[router]["pim"]: + continue + + result = _create_pim_config( + tgen, topo, input_dict, router, build, load_config + ) + if result is not True: + return False + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +def _create_pim_config(tgen, topo, input_dict, router, build=False, load_config=False): + """ + Helper API to create pim configuration. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `router` : router id to be configured. + * `build` : Only for initial setup phase this is set as True. + + Returns + ------- + True or False + """ + + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + try: + + pim_data = input_dict[router]["pim"] + + for dut in tgen.routers(): + if "pim" not in input_dict[router]: + continue + + for destLink, data in topo[dut]["links"].items(): + if "pim" not in data: + continue + + if "rp" in pim_data: + config_data = [] + rp_data = pim_data["rp"] + + for rp_dict in deepcopy(rp_data): + # ip address of RP + if "rp_addr" not in rp_dict and build: + logger.error( + "Router %s: 'ip address of RP' not " + "present in input_dict/JSON", + router, + ) + + return False + rp_addr = rp_dict.setdefault("rp_addr", None) + + # Keep alive Timer + keep_alive_timer = rp_dict.setdefault("keep_alive_timer", None) + + # Group Address range to cover + if "group_addr_range" not in rp_dict and build: + logger.error( + "Router %s:'Group Address range to cover'" + " not present in input_dict/JSON", + router, + ) + + return False + group_addr_range = rp_dict.setdefault("group_addr_range", None) + + # Group prefix-list filter + prefix_list = rp_dict.setdefault("prefix_list", None) + + # Delete rp config + del_action = rp_dict.setdefault("delete", False) + + if keep_alive_timer: + cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer) + config_data.append(cmd) + + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if rp_addr: + if group_addr_range: + if type(group_addr_range) is not list: + group_addr_range = [group_addr_range] + + for grp_addr in group_addr_range: + cmd = "ip pim rp {} {}".format(rp_addr, grp_addr) + config_data.append(cmd) + + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if prefix_list: + cmd = "ip pim rp {} prefix-list {}".format( + rp_addr, prefix_list + ) + config_data.append(cmd) + + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + result = create_common_configuration( + tgen, dut, config_data, "pim", build, load_config + ) + if result is not True: + return False + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +def create_igmp_config(tgen, topo, input_dict=None, build=False): + """ + API to configure igmp on router + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from + testcase + * `build` : Only for initial setup phase this is set as True. + + Usage + ----- + input_dict = { + "r1": { + "igmp": { + "interfaces": { + "r1-r0-eth0" :{ + "igmp":{ + "version": "2", + "delete": True + "query": { + "query-interval" : 100, + "query-max-response-time": 200 + } + } + } + } + } + } + } + + Returns + ------- + True or False + """ + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + result = False + if not input_dict: + input_dict = deepcopy(topo) + else: + topo = topo["routers"] + input_dict = deepcopy(input_dict) + for router in input_dict.keys(): + if "igmp" not in input_dict[router]: + logger.debug("Router %s: 'igmp' is not present in " "input_dict", router) + continue + + igmp_data = input_dict[router]["igmp"] + + if "interfaces" in igmp_data: + config_data = [] + intf_data = igmp_data["interfaces"] + + for intf_name in intf_data.keys(): + cmd = "interface {}".format(intf_name) + config_data.append(cmd) + protocol = "igmp" + del_action = intf_data[intf_name]["igmp"].setdefault("delete", False) + cmd = "ip igmp" + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + del_attr = intf_data[intf_name]["igmp"].setdefault("delete_attr", False) + for attribute, data in intf_data[intf_name]["igmp"].items(): + if attribute == "version": + cmd = "ip {} {} {}".format(protocol, attribute, data) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if attribute == "join": + for group in data: + cmd = "ip {} {} {}".format(protocol, attribute, group) + if del_attr: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if attribute == "query": + for query, value in data.items(): + if query != "delete": + cmd = "ip {} {} {}".format(protocol, query, value) + + if "delete" in intf_data[intf_name][protocol]["query"]: + cmd = "no {}".format(cmd) + + config_data.append(cmd) + try: + + result = create_common_configuration( + tgen, router, config_data, "interface_config", build=build + ) + except InvalidCLIError: + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +def _enable_disable_pim(tgen, topo, input_dict, router, build=False): + """ + Helper API to enable or disable pim on interfaces + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `router` : router id to be configured. + * `build` : Only for initial setup phase this is set as True. + + Returns + ------- + True or False + """ + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + try: + config_data = [] + + enable_flag = True + # Disable pim on interface + if "pim" in input_dict[router]: + if "disable" in input_dict[router]["pim"]: + enable_flag = False + interfaces = input_dict[router]["pim"]["disable"] + + if type(interfaces) is not list: + interfaces = [interfaces] + + for interface in interfaces: + cmd = "interface {}".format(interface) + config_data.append(cmd) + config_data.append("no ip pim") + + # Enable pim on interface + if enable_flag: + for destRouterLink, data in sorted(topo[router]["links"].items()): + if "pim" in data and data["pim"] == "enable": + + # Loopback interfaces + if "type" in data and data["type"] == "loopback": + interface_name = destRouterLink + else: + interface_name = data["interface"] + + cmd = "interface {}".format(interface_name) + config_data.append(cmd) + config_data.append("ip pim") + + result = create_common_configuration( + tgen, router, config_data, "interface_config", build=build + ) + if result is not True: + return False + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +def add_rp_interfaces_and_pim_config(tgen, topo, interface, rp, rp_mapping): + """ + Add physical interfaces tp RP for all the RPs + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `interface` : RP interface + * `rp` : rp for given topology + * `rp_mapping` : dictionary of all groups and RPs + + Returns + ------- + True or False + """ + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + try: + config_data = [] + + for group, rp_list in rp_mapping.items(): + for _rp in rp_list: + config_data.append("interface {}".format(interface)) + config_data.append("ip address {}".format(_rp)) + config_data.append("ip pim") + + result = create_common_configuration( + tgen, rp, config_data, "interface_config" + ) + if result is not True: + return False + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +def find_rp_details(tgen, topo): + """ + Find who is RP in topology and returns list of RPs + + Parameters: + ----------- + * `tgen` : Topogen object + * `topo` : json file data + + returns: + -------- + errormsg or True + """ + + rp_details = {} + + router_list = tgen.routers() + topo_data = topo["routers"] + + for router in router_list.keys(): + + if "pim" not in topo_data[router]: + continue + + pim_data = topo_data[router]["pim"] + if "rp" in pim_data: + rp_data = pim_data["rp"] + for rp_dict in rp_data: + # ip address of RP + rp_addr = rp_dict["rp_addr"] + + for link, data in topo["routers"][router]["links"].items(): + if data["ipv4"].split("/")[0] == rp_addr: + rp_details[router] = rp_addr + + return rp_details + + +def configure_pim_force_expire(tgen, topo, input_dict, build=False): + """ + Helper API to create pim configuration. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + + Usage + ----- + input_dict ={ + "l1": { + "pim": { + "force_expire":{ + "10.0.10.1": ["255.1.1.1"] + } + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + + Returns + ------- + True or False + """ + + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + try: + + for dut in input_dict.keys(): + if "pim" not in input_dict[dut]: + continue + + pim_data = input_dict[dut]["pim"] + + if "force_expire" in pim_data: + config_data = [] + force_expire_data = pim_data["force_expire"] + + for source, groups in force_expire_data.items(): + if type(groups) is not list: + groups = [groups] + + for group in groups: + cmd = "ip pim force-expire source {} group {}".format( + source, group + ) + config_data.append(cmd) + + result = create_common_configuration( + tgen, dut, config_data, "pim", build=build + ) + if result is not True: + return False + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +############################################# +# Verification APIs +############################################# +@retry(attempts=6, wait=2, return_is_str=True) +def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None): + """ + Verify all PIM neighbors are up and running, config is verified + using "show ip pim neighbor" cli + + Parameters + ---------- + * `tgen`: topogen object + * `topo` : json file data + * `dut` : dut info + * `iface` : link for which PIM nbr need to check + * `nbr_ip` : neighbor ip of interface + + Usage + ----- + result = verify_pim_neighbors(tgen, topo, dut, iface=ens192, nbr_ip=20.1.1.2) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router in tgen.routers(): + if dut is not None and dut != router: + continue + + rnode = tgen.routers()[router] + show_ip_pim_neighbor_json = rnode.vtysh_cmd( + "show ip pim neighbor json", isjson=True + ) + + for destLink, data in topo["routers"][router]["links"].items(): + if iface is not None and iface != data["interface"]: + continue + + if "type" in data and data["type"] == "loopback": + continue + + if "pim" not in data: + continue + + if "pim" in data and data["pim"] == "enable": + local_interface = data["interface"] + + if "-" in destLink: + # Spliting and storing destRouterLink data in tempList + tempList = destLink.split("-") + + # destRouter + destLink = tempList.pop(0) + + # Current Router Link + tempList.insert(0, router) + curRouter = "-".join(tempList) + else: + curRouter = router + if destLink not in topo["routers"]: + continue + data = topo["routers"][destLink]["links"][curRouter] + if "type" in data and data["type"] == "loopback": + continue + + if "pim" not in data: + continue + + logger.info("[DUT: %s]: Verifying PIM neighbor status:", router) + + if "pim" in data and data["pim"] == "enable": + pim_nh_intf_ip = data["ipv4"].split("/")[0] + + # Verifying PIM neighbor + if local_interface in show_ip_pim_neighbor_json: + if show_ip_pim_neighbor_json[local_interface]: + if ( + show_ip_pim_neighbor_json[local_interface][pim_nh_intf_ip][ + "neighbor" + ] + != pim_nh_intf_ip + ): + errormsg = ( + "[DUT %s]: Local interface: %s, PIM" + " neighbor check failed " + "Expected neighbor: %s, Found neighbor:" + " %s" + % ( + router, + local_interface, + pim_nh_intf_ip, + show_ip_pim_neighbor_json[local_interface][ + pim_nh_intf_ip + ]["neighbor"], + ) + ) + return errormsg + + logger.info( + "[DUT %s]: Local interface: %s, Found" + " expected PIM neighbor %s", + router, + local_interface, + pim_nh_intf_ip, + ) + else: + errormsg = ( + "[DUT %s]: Local interface: %s, and" + "interface ip: %s is not found in " + "PIM neighbor " % (router, local_interface, pim_nh_intf_ip) + ) + return errormsg + else: + errormsg = ( + "[DUT %s]: Local interface: %s, is not " + "present in PIM neighbor " % (router, local_interface) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=21, wait=2, return_is_str=True) +def verify_igmp_groups(tgen, dut, interface, group_addresses): + """ + Verify IGMP groups are received from an intended interface + by running "show ip igmp groups" command + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + * `interface`: interface, from which IGMP groups would be received + * `group_addresses`: IGMP group address + + Usage + ----- + dut = "r1" + interface = "r1-r0-eth0" + group_address = "225.1.1.1" + result = verify_igmp_groups(tgen, dut, interface, group_address) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying IGMP groups received:", dut) + show_ip_igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True) + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + if interface in show_ip_igmp_json: + show_ip_igmp_json = show_ip_igmp_json[interface]["groups"] + else: + errormsg = ( + "[DUT %s]: Verifying IGMP group received" + " from interface %s [FAILED]!! " % (dut, interface) + ) + return errormsg + + found = False + for grp_addr in group_addresses: + for index in show_ip_igmp_json: + if index["group"] == grp_addr: + found = True + break + if found is not True: + errormsg = ( + "[DUT %s]: Verifying IGMP group received" + " from interface %s [FAILED]!! " + " Expected not found: %s" % (dut, interface, grp_addr) + ) + return errormsg + + logger.info( + "[DUT %s]: Verifying IGMP group %s received " + "from interface %s [PASSED]!! ", + dut, + grp_addr, + interface, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=31, wait=2, return_is_str=True) +def verify_upstream_iif( + tgen, dut, iif, src_address, group_addresses, joinState=None, refCount=1 +): + """ + Verify upstream inbound interface is updated correctly + by running "show ip pim upstream" cli + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + * `iif`: inbound interface + * `src_address`: source address + * `group_addresses`: IGMP group address + * `joinState`: upstream join state + * `refCount`: refCount value + + Usage + ----- + dut = "r1" + iif = "r1-r0-eth0" + src_address = "*" + group_address = "225.1.1.1" + result = verify_upstream_iif(tgen, dut, iif, src_address, group_address, + state, refCount) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + + logger.info( + "[DUT: %s]: Verifying upstream Inbound Interface" " for IGMP groups received:", + dut, + ) + show_ip_pim_upstream_json = run_frr_cmd( + rnode, "show ip pim upstream json", isjson=True + ) + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + if type(iif) is not list: + iif = [iif] + + for grp_addr in group_addresses: + # Verify group address + if grp_addr not in show_ip_pim_upstream_json: + errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % ( + dut, + grp_addr, + ) + return errormsg + group_addr_json = show_ip_pim_upstream_json[grp_addr] + + # Verify source address + if src_address not in group_addr_json: + errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % ( + dut, + src_address, + grp_addr, + ) + return errormsg + + # Verify Inbound Interface + found = False + for in_interface in iif: + if group_addr_json[src_address]["inboundInterface"] == in_interface: + if refCount > 0: + logger.info( + "[DUT %s]: Verifying refCount " + "for (%s,%s) [PASSED]!! " + " Found Expected: %s", + dut, + src_address, + grp_addr, + group_addr_json[src_address]["refCount"], + ) + found = True + if found: + if joinState is None: + if group_addr_json[src_address]["joinState"] != "Joined": + errormsg = ( + "[DUT %s]: Verifying iif " + "(Inbound Interface) for (%s,%s) and" + " joinState :%s [FAILED]!! " + " Expected: %s, Found: %s" + % ( + dut, + src_address, + grp_addr, + group_addr_json[src_address]["joinState"], + in_interface, + group_addr_json[src_address]["inboundInterface"], + ) + ) + return errormsg + + elif group_addr_json[src_address]["joinState"] != joinState: + errormsg = ( + "[DUT %s]: Verifying iif " + "(Inbound Interface) for (%s,%s) and" + " joinState :%s [FAILED]!! " + " Expected: %s, Found: %s" + % ( + dut, + src_address, + grp_addr, + group_addr_json[src_address]["joinState"], + in_interface, + group_addr_json[src_address]["inboundInterface"], + ) + ) + return errormsg + + logger.info( + "[DUT %s]: Verifying iif(Inbound Interface)" + " for (%s,%s) and joinState is %s [PASSED]!! " + " Found Expected: (%s)", + dut, + src_address, + grp_addr, + group_addr_json[src_address]["joinState"], + group_addr_json[src_address]["inboundInterface"], + ) + if not found: + errormsg = ( + "[DUT %s]: Verifying iif " + "(Inbound Interface) for (%s, %s) " + "[FAILED]!! " + " Expected: %s, Found: %s" + % ( + dut, + src_address, + grp_addr, + in_interface, + group_addr_json[src_address]["inboundInterface"], + ) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=6, wait=2, return_is_str=True) +def verify_join_state_and_timer(tgen, dut, iif, src_address, group_addresses): + """ + Verify join state is updated correctly and join timer is + running with the help of "show ip pim upstream" cli + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + * `iif`: inbound interface + * `src_address`: source address + * `group_addresses`: IGMP group address + + Usage + ----- + dut = "r1" + iif = "r1-r0-eth0" + group_address = "225.1.1.1" + result = verify_join_state_and_timer(tgen, dut, iif, group_address) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + errormsg = "" + + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + + logger.info( + "[DUT: %s]: Verifying Join state and Join Timer" " for IGMP groups received:", + dut, + ) + show_ip_pim_upstream_json = run_frr_cmd( + rnode, "show ip pim upstream json", isjson=True + ) + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + for grp_addr in group_addresses: + # Verify group address + if grp_addr not in show_ip_pim_upstream_json: + errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % ( + dut, + grp_addr, + ) + return errormsg + + group_addr_json = show_ip_pim_upstream_json[grp_addr] + + # Verify source address + if src_address not in group_addr_json: + errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % ( + dut, + src_address, + grp_addr, + ) + return errormsg + + # Verify join state + joinState = group_addr_json[src_address]["joinState"] + if joinState != "Joined": + error = ( + "[DUT %s]: Verifying join state for" + " (%s,%s) [FAILED]!! " + " Expected: %s, Found: %s" + % (dut, src_address, grp_addr, "Joined", joinState) + ) + errormsg = errormsg + "\n" + str(error) + else: + logger.info( + "[DUT %s]: Verifying join state for" + " (%s,%s) [PASSED]!! " + " Found Expected: %s", + dut, + src_address, + grp_addr, + joinState, + ) + + # Verify join timer + joinTimer = group_addr_json[src_address]["joinTimer"] + if not re.match(r"(\d{2}):(\d{2}):(\d{2})", joinTimer): + error = ( + "[DUT %s]: Verifying join timer for" + " (%s,%s) [FAILED]!! " + " Expected: %s, Found: %s", + dut, + src_address, + grp_addr, + "join timer should be running", + joinTimer, + ) + errormsg = errormsg + "\n" + str(error) + else: + logger.info( + "[DUT %s]: Verifying join timer is running" + " for (%s,%s) [PASSED]!! " + " Found Expected: %s", + dut, + src_address, + grp_addr, + joinTimer, + ) + + if errormsg != "": + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=41, wait=2, return_is_dict=True) +def verify_ip_mroutes( + tgen, dut, src_address, group_addresses, iif, oil, return_uptime=False, mwait=0 +): + """ + Verify ip mroutes and make sure (*, G)/(S, G) is present in mroutes + by running "show ip pim upstream" cli + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + * `src_address`: source address + * `group_addresses`: IGMP group address + * `iif`: Incoming interface + * `oil`: Outgoing interface + * `return_uptime`: If True, return uptime dict, default is False + * `mwait`: Wait time, default is 0 + + + Usage + ----- + dut = "r1" + group_address = "225.1.1.1" + result = verify_ip_mroutes(tgen, dut, src_address, group_address) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + + if return_uptime: + logger.info("Sleeping for %s sec..", mwait) + sleep(mwait) + + logger.info("[DUT: %s]: Verifying ip mroutes", dut) + show_ip_mroute_json = run_frr_cmd(rnode, "show ip mroute json", isjson=True) + + if return_uptime: + uptime_dict = {} + + if bool(show_ip_mroute_json) == False: + error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut) + return error_msg + + if not isinstance(group_addresses, list): + group_addresses = [group_addresses] + + if not isinstance(iif, list) and iif is not "none": + iif = [iif] + + if not isinstance(oil, list) and oil is not "none": + oil = [oil] + + for grp_addr in group_addresses: + if grp_addr not in show_ip_mroute_json: + errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % ( + dut, + src_address, + grp_addr, + ) + return errormsg + else: + if return_uptime: + uptime_dict[grp_addr] = {} + + group_addr_json = show_ip_mroute_json[grp_addr] + + if src_address not in group_addr_json: + errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % ( + dut, + src_address, + grp_addr, + ) + return errormsg + else: + if return_uptime: + uptime_dict[grp_addr][src_address] = {} + + mroutes = group_addr_json[src_address] + + if mroutes["installed"] != 0: + logger.info( + "[DUT %s]: mroute (%s,%s) is installed", dut, src_address, grp_addr + ) + + if "oil" not in mroutes: + if oil == "none" and mroutes["iif"] in iif: + logger.info( + "[DUT %s]: Verifying (%s, %s) mroute," + " [PASSED]!! Found Expected: " + "(iif: %s, oil: %s, installed: (%s,%s))", + dut, + src_address, + grp_addr, + mroutes["iif"], + oil, + src_address, + grp_addr, + ) + else: + errormsg = ( + "[DUT %s]: Verifying (%s, %s) mroute," + " [FAILED]!! " + "Expected: (oil: %s, installed:" + " (%s,%s)) Found: ( oil: none, " + "installed: (%s,%s))" + % ( + dut, + src_address, + grp_addr, + oil, + src_address, + grp_addr, + src_address, + grp_addr, + ) + ) + + return errormsg + + else: + found = False + for route, data in mroutes["oil"].items(): + if route in oil and route != "pimreg": + if ( + data["source"] == src_address + and data["group"] == grp_addr + and data["inboundInterface"] in iif + and data["outboundInterface"] in oil + ): + if return_uptime: + + uptime_dict[grp_addr][src_address] = data["upTime"] + + logger.info( + "[DUT %s]: Verifying (%s, %s)" + " mroute, [PASSED]!! " + "Found Expected: " + "(iif: %s, oil: %s, installed:" + " (%s,%s)", + dut, + src_address, + grp_addr, + data["inboundInterface"], + data["outboundInterface"], + data["source"], + data["group"], + ) + found = True + break + else: + continue + + if not found: + errormsg = ( + "[DUT %s]: Verifying (%s, %s)" + " mroute [FAILED]!! " + "Expected in: (iif: %s, oil: %s," + " installed: (%s,%s)) Found: " + "(iif: %s, oil: %s, " + "installed: (%s,%s))" + % ( + dut, + src_address, + grp_addr, + iif, + oil, + src_address, + grp_addr, + data["inboundInterface"], + data["outboundInterface"], + data["source"], + data["group"], + ) + ) + return errormsg + + else: + errormsg = "[DUT %s]: mroute (%s,%s) is not installed" % ( + dut, + src_address, + grp_addr, + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True if return_uptime == False else uptime_dict + + +@retry(attempts=31, wait=2, return_is_str=True) +def verify_pim_rp_info( + tgen, topo, dut, group_addresses, oif=None, rp=None, source=None, iamrp=None +): + """ + Verify pim rp info by running "show ip pim rp-info" cli + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: JSON file handler + * `dut`: device under test + * `group_addresses`: IGMP group address + * `oif`: outbound interface name + * `rp`: RP address + * `source`: Source of RP + * `iamrp`: User defined RP + + Usage + ----- + dut = "r1" + result = verify_pim_rp_info(tgen, topo, dut, group_address, + rp=rp, source="BSR") + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying ip rp info", dut) + show_ip_rp_info_json = run_frr_cmd(rnode, "show ip pim rp-info json", isjson=True) + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + if type(oif) is not list: + oif = [oif] + + for grp_addr in group_addresses: + if rp is None: + rp_details = find_rp_details(tgen, topo) + + if dut in rp_details: + iamRP = True + else: + iamRP = False + else: + show_ip_route_json = run_frr_cmd( + rnode, "show ip route connected json", isjson=True + ) + for _rp in show_ip_route_json.keys(): + if rp == _rp.split("/")[0]: + iamRP = True + break + else: + iamRP = False + + if rp not in show_ip_rp_info_json: + errormsg = "[DUT %s]: Verifying rp-info" "for rp_address %s [FAILED]!! " % ( + dut, + rp, + ) + return errormsg + else: + group_addr_json = show_ip_rp_info_json[rp] + + for rp_json in group_addr_json: + if oif is not None: + found = False + if rp_json["outboundInterface"] not in oif: + errormsg = ( + "[DUT %s]: Verifying OIF " + "for group %s and RP %s [FAILED]!! " + "Expected interfaces: (%s)," + " Found: (%s)" + % (dut, grp_addr, rp, oif, rp_json["outboundInterface"]) + ) + return errormsg + + logger.info( + "[DUT %s]: Verifying OIF " + "for group %s and RP %s [PASSED]!! " + "Found Expected: (%s)" + % (dut, grp_addr, rp, rp_json["outboundInterface"]) + ) + + if source is not None: + if rp_json["source"] != source: + errormsg = ( + "[DUT %s]: Verifying SOURCE " + "for group %s and RP %s [FAILED]!! " + "Expected: (%s)," + " Found: (%s)" % (dut, grp_addr, rp, source, rp_json["source"]) + ) + return errormsg + + logger.info( + "[DUT %s]: Verifying SOURCE " + "for group %s and RP %s [PASSED]!! " + "Found Expected: (%s)" % (dut, grp_addr, rp, rp_json["source"]) + ) + + if rp_json["group"] == grp_addr and iamrp is not None: + if iamRP: + if rp_json["iAmRP"]: + logger.info( + "[DUT %s]: Verifying group " + "and iAmRP [PASSED]!!" + " Found Expected: (%s, %s:%s)", + dut, + grp_addr, + "iAmRP", + rp_json["iAmRP"], + ) + else: + errormsg = ( + "[DUT %s]: Verifying group" + "%s and iAmRP [FAILED]!! " + "Expected: (iAmRP: %s)," + " Found: (iAmRP: %s)" + % (dut, grp_addr, "true", rp_json["iAmRP"]) + ) + return errormsg + + if not iamRP: + if rp_json["iAmRP"] == False: + logger.info( + "[DUT %s]: Verifying group " + "and iAmNotRP [PASSED]!!" + " Found Expected: (%s, %s:%s)", + dut, + grp_addr, + "iAmRP", + rp_json["iAmRP"], + ) + else: + errormsg = ( + "[DUT %s]: Verifying group" + "%s and iAmRP [FAILED]!! " + "Expected: (iAmRP: %s)," + " Found: (iAmRP: %s)" + % (dut, grp_addr, "false", rp_json["iAmRP"]) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=31, wait=2, return_is_str=True) +def verify_pim_state( + tgen, dut, iif, oil, group_addresses, src_address=None, installed_fl=None +): + """ + Verify pim state by running "show ip pim state" cli + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + * `iif`: inbound interface + * `oil`: outbound interface + * `group_addresses`: IGMP group address + * `src_address`: source address, default = None + * installed_fl` : Installed flag + + Usage + ----- + dut = "r1" + iif = "r1-r3-eth1" + oil = "r1-r0-eth0" + group_address = "225.1.1.1" + result = verify_pim_state(tgen, dut, iif, oil, group_address) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying pim state", dut) + show_pim_state_json = run_frr_cmd(rnode, "show ip pim state json", isjson=True) + + if installed_fl is None: + installed_fl = 1 + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + for grp_addr in group_addresses: + if src_address is None: + src_address = "*" + pim_state_json = show_pim_state_json[grp_addr][src_address] + else: + pim_state_json = show_pim_state_json[grp_addr][src_address] + + if pim_state_json["Installed"] == installed_fl: + logger.info( + "[DUT %s]: group %s is installed flag: %s", + dut, + grp_addr, + pim_state_json["Installed"], + ) + for interface, data in pim_state_json[iif].items(): + if interface != oil: + continue + + # Verify iif, oil and installed state + if ( + data["group"] == grp_addr + and data["installed"] == installed_fl + and data["inboundInterface"] == iif + and data["outboundInterface"] == oil + ): + logger.info( + "[DUT %s]: Verifying pim state for group" + " %s [PASSED]!! Found Expected: " + "(iif: %s, oil: %s, installed: %s) ", + dut, + grp_addr, + data["inboundInterface"], + data["outboundInterface"], + data["installed"], + ) + else: + errormsg = ( + "[DUT %s]: Verifying pim state for group" + " %s, [FAILED]!! Expected: " + "(iif: %s, oil: %s, installed: %s) ", + "Found: (iif: %s, oil: %s, installed: %s)" + % ( + dut, + grp_addr, + iif, + oil, + "1", + data["inboundInterface"], + data["outboundInterface"], + data["installed"], + ), + ) + return errormsg + else: + errormsg = "[DUT %s]: %s install flag value not as expected" % ( + dut, + grp_addr, + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def verify_pim_interface_traffic(tgen, input_dict): + """ + Verify ip pim interface traffice by running + "show ip pim interface traffic" cli + + Parameters + ---------- + * `tgen`: topogen object + * `input_dict(dict)`: defines DUT, what and from which interfaces + traffic needs to be verified + Usage + ----- + input_dict = { + "r1": { + "r1-r0-eth0": { + "helloRx": 0, + "helloTx": 1, + "joinRx": 0, + "joinTx": 0 + } + } + } + + result = verify_pim_interface_traffic(tgen, input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + output_dict = {} + for dut in input_dict.keys(): + if dut not in tgen.routers(): + continue + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying pim interface traffic", dut) + show_pim_intf_traffic_json = run_frr_cmd( + rnode, "show ip pim interface traffic json", isjson=True + ) + + output_dict[dut] = {} + for intf, data in input_dict[dut].items(): + interface_json = show_pim_intf_traffic_json[intf] + for state in data: + + # Verify Tx/Rx + if state in interface_json: + output_dict[dut][state] = interface_json[state] + else: + errormsg = ( + "[DUT %s]: %s is not present" + "for interface %s [FAILED]!! " % (dut, state, intf) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return output_dict + + +@retry(attempts=21, wait=2, return_is_str=True) +def verify_pim_interface(tgen, topo, dut, interface=None, interface_ip=None): + """ + Verify all PIM interface are up and running, config is verified + using "show ip pim interface" cli + + Parameters + ---------- + * `tgen`: topogen object + * `topo` : json file data + * `dut` : device under test + * `interface` : interface name + * `interface_ip` : interface ip address + + Usage + ----- + result = verify_pim_interfacetgen, topo, dut, interface=ens192, interface_ip=20.1.1.1) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router in tgen.routers(): + if router != dut: + continue + + logger.info("[DUT: %s]: Verifying PIM interface status:", dut) + + rnode = tgen.routers()[dut] + show_ip_pim_interface_json = rnode.\ + vtysh_cmd("show ip pim interface json", isjson=True) + + logger.info("show_ip_pim_interface_json: \n %s", + show_ip_pim_interface_json) + + if interface_ip: + if interface in show_ip_pim_interface_json: + pim_intf_json = show_ip_pim_interface_json[interface] + if pim_intf_json["address"] != interface_ip: + errormsg = ("[DUT %s]: PIM interface " + "ip is not correct " + "[FAILED]!! Expected : %s, Found : %s" + %(dut, pim_intf_json["address"],interface_ip)) + return errormsg + else: + logger.info("[DUT %s]: PIM interface " + "ip is correct " + "[Passed]!! Expected : %s, Found : %s" + %(dut, pim_intf_json["address"],interface_ip)) + return True + else: + for destLink, data in topo["routers"][dut]["links"].items(): + if "type" in data and data["type"] == "loopback": + continue + + if "pim" in data and data["pim"] == "enable": + pim_interface = data["interface"] + pim_intf_ip = data["ipv4"].split("/")[0] + + if pim_interface in show_ip_pim_interface_json: + pim_intf_json = show_ip_pim_interface_json\ + [pim_interface] + + # Verifying PIM interface + if pim_intf_json["address"] != pim_intf_ip and \ + pim_intf_json["state"] != "up": + errormsg = ("[DUT %s]: PIM interface: %s " + "PIM interface ip: %s, status check " + "[FAILED]!! Expected : %s, Found : %s" + %(dut, pim_interface, pim_intf_ip, + pim_interface, pim_intf_json["state"])) + return errormsg + + logger.info("[DUT %s]: PIM interface: %s, " + "interface ip: %s, status: %s" + " [PASSED]!!", + dut, pim_interface, pim_intf_ip, + pim_intf_json["state"]) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def clear_ip_pim_interface_traffic(tgen, topo): + """ + Clear ip pim interface traffice by running + "clear ip pim interface traffic" cli + + Parameters + ---------- + * `tgen`: topogen object + Usage + ----- + + result = clear_ip_pim_interface_traffic(tgen, topo) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for dut in tgen.routers(): + if "pim" not in topo["routers"][dut]: + continue + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Clearing pim interface traffic", dut) + result = run_frr_cmd(rnode, "clear ip pim interface traffic") + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + return True + + +def clear_ip_pim_interfaces(tgen, dut): + """ + Clear ip pim interface by running + "clear ip pim interfaces" cli + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: Device Under Test + Usage + ----- + + result = clear_ip_pim_interfaces(tgen, dut) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + nh_before_clear = {} + nh_after_clear = {} + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verify pim neighbor before pim" " neighbor clear", dut) + # To add uptime initially + sleep(10) + run_json_before = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True) + + for key, value in run_json_before.items(): + if bool(value): + for _key, _value in value.items(): + nh_before_clear[key] = _value["upTime"] + + # Clearing PIM neighbors + logger.info("[DUT: %s]: Clearing pim interfaces", dut) + run_frr_cmd(rnode, "clear ip pim interfaces") + + logger.info("[DUT: %s]: Verify pim neighbor after pim" " neighbor clear", dut) + + found = False + + # Waiting for maximum 60 sec + fail_intf = [] + for retry in range(1, 13): + logger.info("[DUT: %s]: Waiting for 5 sec for PIM neighbors" " to come up", dut) + sleep(5) + run_json_after = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True) + found = True + for pim_intf in nh_before_clear.keys(): + if pim_intf not in run_json_after or not run_json_after[pim_intf]: + found = False + fail_intf.append(pim_intf) + + if found is True: + break + else: + errormsg = ( + "[DUT: %s]: pim neighborship is not formed for %s" + "after clear_ip_pim_interfaces %s [FAILED!!]", + dut, + fail_intf, + ) + return errormsg + + for key, value in run_json_after.items(): + if bool(value): + for _key, _value in value.items(): + nh_after_clear[key] = _value["upTime"] + + # Verify uptime for neighbors + for pim_intf in nh_before_clear.keys(): + d1 = datetime.datetime.strptime(nh_before_clear[pim_intf], "%H:%M:%S") + d2 = datetime.datetime.strptime(nh_after_clear[pim_intf], "%H:%M:%S") + if d2 >= d1: + errormsg = ( + "[DUT: %s]: PIM neighborship is not cleared for", + " interface %s [FAILED!!]", + dut, + pim_intf, + ) + + logger.info("[DUT: %s]: PIM neighborship is cleared [PASSED!!]") + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + return True + + +def clear_ip_igmp_interfaces(tgen, dut): + """ + Clear ip igmp interfaces by running + "clear ip igmp interfaces" cli + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + + Usage + ----- + dut = "r1" + result = clear_ip_igmp_interfaces(tgen, dut) + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + group_before_clear = {} + group_after_clear = {} + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: IGMP group uptime before clear" " igmp groups:", dut) + igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True) + + total_groups_before_clear = igmp_json["totalGroups"] + + for key, value in igmp_json.items(): + if type(value) is not dict: + continue + + groups = value["groups"] + group = groups[0]["group"] + uptime = groups[0]["uptime"] + group_before_clear[group] = uptime + + logger.info("[DUT: %s]: Clearing ip igmp interfaces", dut) + result = run_frr_cmd(rnode, "clear ip igmp interfaces") + + # Waiting for maximum 60 sec + for retry in range(1, 13): + logger.info( + "[DUT: %s]: Waiting for 5 sec for igmp interfaces" " to come up", dut + ) + sleep(5) + igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True) + + total_groups_after_clear = igmp_json["totalGroups"] + + if total_groups_before_clear == total_groups_after_clear: + break + + for key, value in igmp_json.items(): + if type(value) is not dict: + continue + + groups = value["groups"] + group = groups[0]["group"] + uptime = groups[0]["uptime"] + group_after_clear[group] = uptime + + # Verify uptime for groups + for group in group_before_clear.keys(): + d1 = datetime.datetime.strptime(group_before_clear[group], "%H:%M:%S") + d2 = datetime.datetime.strptime(group_after_clear[group], "%H:%M:%S") + if d2 >= d1: + errormsg = ("[DUT: %s]: IGMP group is not cleared", " [FAILED!!]", dut) + + logger.info("[DUT: %s]: IGMP group is cleared [PASSED!!]") + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + return True + + +@retry(attempts=10, wait=2, return_is_str=True) +def clear_ip_mroute_verify(tgen, dut): + """ + Clear ip mroute by running "clear ip mroute" cli and verify + mroutes are up again after mroute clear + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: Device Under Test + Usage + ----- + + result = clear_ip_mroute_verify(tgen, dut) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + mroute_before_clear = {} + mroute_after_clear = {} + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: IP mroutes uptime before clear", dut) + mroute_json_1 = run_frr_cmd(rnode, "show ip mroute json", isjson=True) + + for group in mroute_json_1.keys(): + mroute_before_clear[group] = {} + for key in mroute_json_1[group].keys(): + for _key, _value in mroute_json_1[group][key]["oil"].items(): + if _key != "pimreg": + mroute_before_clear[group][key] = _value["upTime"] + + logger.info("[DUT: %s]: Clearing ip mroute", dut) + result = run_frr_cmd(rnode, "clear ip mroute") + + # RFC 3376: 8.2. Query Interval - Default: 125 seconds + # So waiting for maximum 130 sec to get the igmp report + for retry in range(1, 26): + logger.info("[DUT: %s]: Waiting for 2 sec for mroutes" " to come up", dut) + sleep(5) + keys_json1 = mroute_json_1.keys() + mroute_json_2 = run_frr_cmd(rnode, "show ip mroute json", isjson=True) + + if bool(mroute_json_2): + keys_json2 = mroute_json_2.keys() + + for group in mroute_json_2.keys(): + flag = False + for key in mroute_json_2[group].keys(): + if "oil" not in mroute_json_2[group]: + continue + + for _key, _value in mroute_json_2[group][key]["oil"].items(): + if _key != "pimreg" and keys_json1 == keys_json2: + break + flag = True + if flag: + break + else: + continue + + for group in mroute_json_2.keys(): + mroute_after_clear[group] = {} + for key in mroute_json_2[group].keys(): + for _key, _value in mroute_json_2[group][key]["oil"].items(): + if _key != "pimreg": + mroute_after_clear[group][key] = _value["upTime"] + + # Verify uptime for mroute + for group in mroute_before_clear.keys(): + for source in mroute_before_clear[group].keys(): + if set(mroute_before_clear[group]) != set(mroute_after_clear[group]): + errormsg = ( + "[DUT: %s]: mroute (%s, %s) has not come" + " up after mroute clear [FAILED!!]" % (dut, source, group) + ) + return errormsg + + d1 = datetime.datetime.strptime( + mroute_before_clear[group][source], "%H:%M:%S" + ) + d2 = datetime.datetime.strptime( + mroute_after_clear[group][source], "%H:%M:%S" + ) + if d2 >= d1: + errormsg = "[DUT: %s]: IP mroute is not cleared" " [FAILED!!]" % (dut) + + logger.info("[DUT: %s]: IP mroute is cleared [PASSED!!]", dut) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + return True + + +def clear_ip_mroute(tgen, dut=None): + """ + Clear ip mroute by running "clear ip mroute" cli + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test, default None + + Usage + ----- + clear_ip_mroute(tgen, dut) + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + router_list = tgen.routers() + for router, rnode in router_list.items(): + if dut is not None and router != dut: + continue + + logger.debug("[DUT: %s]: Clearing ip mroute", router) + rnode.vtysh_cmd("clear ip mroute") + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + +def reconfig_interfaces(tgen, topo, senderRouter, receiverRouter, packet=None): + """ + Configure interface ip for sender and receiver routers + as per bsr packet + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `senderRouter` : Sender router + * `receiverRouter` : Receiver router + * `packet` : BSR packet in raw format + + Returns + ------- + True or False + """ + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + try: + config_data = [] + + src_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["src_ip"] + dest_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["dest_ip"] + + for destLink, data in topo["routers"][senderRouter]["links"].items(): + if "type" in data and data["type"] == "loopback": + continue + + if "pim" in data and data["pim"] == "enable": + sender_interface = data["interface"] + sender_interface_ip = data["ipv4"] + + config_data.append("interface {}".format(sender_interface)) + config_data.append("no ip address {}".format(sender_interface_ip)) + config_data.append("ip address {}".format(src_ip)) + + result = create_common_configuration( + tgen, senderRouter, config_data, "interface_config" + ) + if result is not True: + return False + + config_data = [] + links = topo["routers"][destLink]["links"] + pim_neighbor = {key: links[key] for key in [senderRouter]} + + data = pim_neighbor[senderRouter] + if "type" in data and data["type"] == "loopback": + continue + + if "pim" in data and data["pim"] == "enable": + receiver_interface = data["interface"] + receiver_interface_ip = data["ipv4"] + + config_data.append("interface {}".format(receiver_interface)) + config_data.append("no ip address {}".format(receiver_interface_ip)) + config_data.append("ip address {}".format(dest_ip)) + + result = create_common_configuration( + tgen, receiverRouter, config_data, "interface_config" + ) + if result is not True: + return False + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: reconfig_interfaces()") + return result + + +def add_rp_interfaces_and_pim_config(tgen, topo, interface, rp, rp_mapping): + """ + Add physical interfaces tp RP for all the RPs + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `interface` : RP interface + * `rp` : rp for given topology + * `rp_mapping` : dictionary of all groups and RPs + + Returns + ------- + True or False + """ + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + try: + config_data = [] + + for group, rp_list in rp_mapping.items(): + for _rp in rp_list: + config_data.append("interface {}".format(interface)) + config_data.append("ip address {}".format(_rp)) + config_data.append("ip pim") + + result = create_common_configuration( + tgen, rp, config_data, "interface_config" + ) + if result is not True: + return False + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: add_rp_interfaces_and_pim_config()") + return result + + +def scapy_send_bsr_raw_packet( + tgen, topo, senderRouter, receiverRouter, packet=None, interval=1, count=1 +): + """ + Using scapy Raw() method to send BSR raw packet from one FRR + to other + + Parameters: + ----------- + * `tgen` : Topogen object + * `topo` : json file data + * `senderRouter` : Sender router + * `receiverRouter` : Receiver router + * `packet` : BSR packet in raw format + * `interval` : Interval between the packets + * `count` : Number of packets to be sent + + returns: + -------- + errormsg or True + """ + + global CWD + result = "" + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + rnode = tgen.routers()[senderRouter] + + for destLink, data in topo["routers"][senderRouter]["links"].items(): + if "type" in data and data["type"] == "loopback": + continue + + if "pim" in data and data["pim"] == "enable": + sender_interface = data["interface"] + + packet = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["data"] + + if interval > 1 or count > 1: + cmd = ( + "nohup /usr/bin/python {}/send_bsr_packet.py '{}' '{}' " + "--interval={} --count={} &".format( + CWD, packet, sender_interface, interval, count + ) + ) + else: + cmd = ( + "/usr/bin/python {}/send_bsr_packet.py '{}' '{}' " + "--interval={} --count={}".format( + CWD, packet, sender_interface, interval, count + ) + ) + + logger.info("Scapy cmd: \n %s", cmd) + result = rnode.run(cmd) + + if result == "": + return result + + logger.debug("Exiting lib API: scapy_send_bsr_raw_packet") + return True + + +def find_rp_from_bsrp_info(tgen, dut, bsr, grp=None): + """ + Find which RP is having lowest prioriy and returns rp IP + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + * `bsr`: BSR address + * 'grp': Group Address + + Usage + ----- + dut = "r1" + result = verify_pim_rp_info(tgen, dut, bsr) + + Returns: + dictionary: group and RP, which has to be installed as per + lowest priority or highest priority + """ + + rp_details = {} + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Fetching rp details from bsrp-info", dut) + bsrp_json = run_frr_cmd(rnode, "show ip pim bsrp-info json", isjson=True) + + if grp not in bsrp_json: + return {} + + for group, rp_data in bsrp_json.items(): + if group == "BSR Address" and bsrp_json["BSR Address"] == bsr: + continue + + if group != grp: + continue + + rp_priority = {} + rp_hash = {} + + for rp, value in rp_data.items(): + if rp == "Pending RP count": + continue + rp_priority[value["Rp Address"]] = value["Rp Priority"] + rp_hash[value["Rp Address"]] = value["Hash Val"] + + priority_dict = dict(zip(rp_priority.values(), rp_priority.keys())) + hash_dict = dict(zip(rp_hash.values(), rp_hash.keys())) + + # RP with lowest priority + if len(priority_dict) != 1: + rp_p, lowest_priority = sorted(rp_priority.items(), key=lambda x: x[1])[0] + rp_details[group] = rp_p + + # RP with highest hash value + if len(priority_dict) == 1: + rp_h, highest_hash = sorted(rp_hash.items(), key=lambda x: x[1])[-1] + rp_details[group] = rp_h + + # RP with highest IP address + if len(priority_dict) == 1 and len(hash_dict) == 1: + rp_details[group] = sorted(rp_priority.keys())[-1] + + return rp_details + + +@retry(attempts=6, wait=2, return_is_str=True) +def verify_pim_grp_rp_source(tgen, topo, dut, grp_addr, rp_source, rpadd=None): + """ + Verify pim rp info by running "show ip pim rp-info" cli + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: JSON file handler + * `dut`: device under test + * `grp_addr`: IGMP group address + * 'rp_source': source from which rp installed + * 'rpadd': rp address + + Usage + ----- + dut = "r1" + group_address = "225.1.1.1" + rp_source = "BSR" + result = verify_pim_rp_and_source(tgen, topo, dut, group_address, rp_source) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying ip rp info", dut) + show_ip_rp_info_json = run_frr_cmd(rnode, "show ip pim rp-info json", isjson=True) + + if rpadd != None: + rp_json = show_ip_rp_info_json[rpadd] + if rp_json[0]["group"] == grp_addr: + if rp_json[0]["source"] == rp_source: + logger.info( + "[DUT %s]: Verifying Group and rp_source [PASSED]" + "Found Expected: %s, %s" + % (dut, rp_json[0]["group"], rp_json[0]["source"]) + ) + return True + else: + errormsg = ( + "[DUT %s]: Verifying Group and rp_source [FAILED]" + "Expected (%s, %s) " + "Found (%s, %s)" + % ( + dut, + grp_addr, + rp_source, + rp_json[0]["group"], + rp_json[0]["source"], + ) + ) + return errormsg + errormsg = ( + "[DUT %s]: Verifying Group and rp_source [FAILED]" + "Expected: %s, %s but not found" % (dut, grp_addr, rp_source) + ) + return errormsg + + for rp in show_ip_rp_info_json: + rp_json = show_ip_rp_info_json[rp] + logger.info("%s", rp_json) + if rp_json[0]["group"] == grp_addr: + if rp_json[0]["source"] == rp_source: + logger.info( + "[DUT %s]: Verifying Group and rp_source [PASSED]" + "Found Expected: %s, %s" + % (dut, rp_json[0]["group"], rp_json[0]["source"]) + ) + return True + else: + errormsg = ( + "[DUT %s]: Verifying Group and rp_source [FAILED]" + "Expected (%s, %s) " + "Found (%s, %s)" + % ( + dut, + grp_addr, + rp_source, + rp_json[0]["group"], + rp_json[0]["source"], + ) + ) + return errormsg + + errormsg = ( + "[DUT %s]: Verifying Group and rp_source [FAILED]" + "Expected: %s, %s but not found" % (dut, grp_addr, rp_source) + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + return errormsg + + +@retry(attempts=31, wait=2, return_is_str=True) +def verify_pim_bsr(tgen, topo, dut, bsr_ip): + """ + Verify all PIM interface are up and running, config is verified + using "show ip pim interface" cli + + Parameters + ---------- + * `tgen`: topogen object + * `topo` : json file data + * `dut` : device under test + * 'bsr' : bsr ip to be verified + + Usage + ----- + result = verify_pim_bsr(tgen, topo, dut, bsr_ip) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router in tgen.routers(): + if router != dut: + continue + + logger.info("[DUT: %s]: Verifying PIM bsr status:", dut) + + rnode = tgen.routers()[dut] + pim_bsr_json = rnode.vtysh_cmd("show ip pim bsr json", isjson=True) + + logger.info("show_ip_pim_bsr_json: \n %s", pim_bsr_json) + + # Verifying PIM bsr + if pim_bsr_json["bsr"] != bsr_ip: + errormsg = ( + "[DUT %s]:" + "bsr status: not found" + "[FAILED]!! Expected : %s, Found : %s" + % (dut, bsr_ip, pim_bsr_json["bsr"]) + ) + return errormsg + + logger.info( + "[DUT %s]:" " bsr status: found, Address :%s" " [PASSED]!!", + dut, + pim_bsr_json["bsr"], + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=31, wait=2, return_is_str=True) +def verify_ip_pim_upstream_rpf(tgen, topo, dut, interface, group_addresses, rp=None): + """ + Verify IP PIM upstream rpf, config is verified + using "show ip pim neighbor" cli + + Parameters + ---------- + * `tgen`: topogen object + * `topo` : json file data + * `dut` : devuce under test + * `interface` : upstream interface + * `group_addresses` : list of group address for which upstream info + needs to be checked + * `rp` : RP address + + Usage + ----- + result = verify_ip_pim_upstream_rpf(gen, topo, dut, interface, + group_addresses, rp=None) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if "pim" in topo["routers"][dut]: + + logger.info("[DUT: %s]: Verifying ip pim upstream rpf:", dut) + + rnode = tgen.routers()[dut] + show_ip_pim_upstream_rpf_json = rnode.vtysh_cmd( + "show ip pim upstream-rpf json", isjson=True + ) + + logger.info( + "show_ip_pim_upstream_rpf_json: \n %s", show_ip_pim_upstream_rpf_json + ) + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + for grp_addr in group_addresses: + for destLink, data in topo["routers"][dut]["links"].items(): + if "type" in data and data["type"] == "loopback": + continue + + if "pim" not in topo["routers"][destLink]: + continue + + # Verify RP info + if rp is None: + rp_details = find_rp_details(tgen, topo) + else: + rp_details = {dut: ip} + rp_details[dut] = rp + + if dut in rp_details: + pim_nh_intf_ip = topo["routers"][dut]["links"]["lo"]["ipv4"].split( + "/" + )[0] + else: + if destLink not in interface: + continue + + links = topo["routers"][destLink]["links"] + pim_neighbor = {key: links[key] for key in [dut]} + + data = pim_neighbor[dut] + if "pim" in data and data["pim"] == "enable": + pim_nh_intf_ip = data["ipv4"].split("/")[0] + + upstream_rpf_json = show_ip_pim_upstream_rpf_json[grp_addr]["*"] + + # Verifying ip pim upstream rpf + if ( + upstream_rpf_json["rpfInterface"] == interface + and upstream_rpf_json["ribNexthop"] != pim_nh_intf_ip + ): + errormsg = ( + "[DUT %s]: Verifying group: %s, " + "rpf interface: %s, " + " rib Nexthop check [FAILED]!!" + "Expected: %s, Found: %s" + % ( + dut, + grp_addr, + interface, + pim_nh_intf_ip, + upstream_rpf_json["ribNexthop"], + ) + ) + return errormsg + + logger.info( + "[DUT %s]: Verifying group: %s," + " rpf interface: %s, " + " rib Nexthop: %s [PASSED]!!", + dut, + grp_addr, + interface, + pim_nh_intf_ip, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def enable_disable_pim_unicast_bsm(tgen, router, intf, enable=True): + """ + Helper API to enable or disable pim bsm on interfaces + + Parameters + ---------- + * `tgen` : Topogen object + * `router` : router id to be configured. + * `intf` : Interface to be configured + * `enable` : this flag denotes if config should be enabled or disabled + + Returns + ------- + True or False + """ + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + try: + config_data = [] + cmd = "interface {}".format(intf) + config_data.append(cmd) + + if enable == True: + config_data.append("ip pim unicast-bsm") + else: + config_data.append("no ip pim unicast-bsm") + + result = create_common_configuration( + tgen, router, config_data, "interface_config", build=False + ) + if result is not True: + return False + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +def enable_disable_pim_bsm(tgen, router, intf, enable=True): + """ + Helper API to enable or disable pim bsm on interfaces + + Parameters + ---------- + * `tgen` : Topogen object + * `router` : router id to be configured. + * `intf` : Interface to be configured + * `enable` : this flag denotes if config should be enabled or disabled + + Returns + ------- + True or False + """ + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + try: + config_data = [] + cmd = "interface {}".format(intf) + config_data.append(cmd) + + if enable is True: + config_data.append("ip pim bsm") + else: + config_data.append("no ip pim bsm") + + result = create_common_configuration( + tgen, router, config_data, "interface_config", build=False + ) + if result is not True: + return False + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +@retry(attempts=31, wait=2, return_is_str=True) +def verify_ip_pim_join(tgen, topo, dut, interface, group_addresses, src_address=None): + """ + Verify ip pim join by running "show ip pim join" cli + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: JSON file handler + * `dut`: device under test + * `interface`: interface name, from which PIM join would come + * `group_addresses`: IGMP group address + * `src_address`: Source address + + Usage + ----- + dut = "r1" + interface = "r1-r0-eth0" + group_address = "225.1.1.1" + result = verify_ip_pim_join(tgen, dut, star, group_address, interface) + + Returns + ------- + errormsg(str) or True + """ + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying pim join", dut) + show_pim_join_json = run_frr_cmd(rnode, "show ip pim join json", isjson=True) + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + for grp_addr in group_addresses: + # Verify if IGMP is enabled in DUT + if "igmp" not in topo["routers"][dut]: + pim_join = True + else: + pim_join = False + + interface_json = show_pim_join_json[interface] + + grp_addr = grp_addr.split("/")[0] + for source, data in interface_json[grp_addr].items(): + + # Verify pim join + if pim_join: + if data["group"] == grp_addr and data["channelJoinName"] == "JOIN": + logger.info( + "[DUT %s]: Verifying pim join for group: %s" + "[PASSED]!! Found Expected: (%s)", + dut, + grp_addr, + data["channelJoinName"], + ) + else: + errormsg = ( + "[DUT %s]: Verifying pim join for group: %s" + "[FAILED]!! Expected: (%s) " + "Found: (%s)" % (dut, grp_addr, "JOIN", data["channelJoinName"]) + ) + return errormsg + + if not pim_join: + if data["group"] == grp_addr and data["channelJoinName"] == "NOINFO": + logger.info( + "[DUT %s]: Verifying pim join for group: %s" + "[PASSED]!! Found Expected: (%s)", + dut, + grp_addr, + data["channelJoinName"], + ) + else: + errormsg = ( + "[DUT %s]: Verifying pim join for group: %s" + "[FAILED]!! Expected: (%s) " + "Found: (%s)" + % (dut, grp_addr, "NOINFO", data["channelJoinName"]) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=31, wait=2, return_is_dict=True) +def verify_igmp_config(tgen, input_dict, stats_return=False): + """ + Verify igmp interface details, verifying following configs: + timerQueryInterval + timerQueryResponseIntervalMsec + lastMemberQueryCount + timerLastMemberQueryMsec + + Parameters + ---------- + * `tgen`: topogen object + * `input_dict` : Input dict data, required to verify + timer + * `stats_return`: If user wants API to return statistics + + Usage + ----- + input_dict ={ + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "query": { + "query-interval" : 200, + "query-max-response-time" : 100 + }, + "statistics": { + "queryV2" : 2, + "reportV2" : 1 + } + } + } + } + } + } + } + result = verify_igmp_config(tgen, input_dict, stats_return) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for dut in input_dict.keys(): + rnode = tgen.routers()[dut] + + for interface, data in input_dict[dut]["igmp"]["interfaces"].items(): + + statistics = False + report = False + if "statistics" in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"]: + statistics = True + cmd = "show ip igmp statistics" + else: + cmd = "show ip igmp" + + logger.info( + "[DUT: %s]: Verifying IGMP interface %s detail:", dut, interface + ) + + if statistics: + if ( + "report" + in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"][ + "statistics" + ] + ): + report = True + + if statistics and report: + show_ip_igmp_intf_json = run_frr_cmd( + rnode, "{} json".format(cmd, interface), isjson=True + ) + intf_detail_json = show_ip_igmp_intf_json["global"] + else: + show_ip_igmp_intf_json = run_frr_cmd( + rnode, "{} interface {} json".format(cmd, interface), isjson=True + ) + + if not report: + if interface not in show_ip_igmp_intf_json: + errormsg = ( + "[DUT %s]: IGMP interface: %s " + " is not present in CLI output " + "[FAILED]!! " % (dut, interface) + ) + return errormsg + + else: + intf_detail_json = show_ip_igmp_intf_json[interface] + + if stats_return: + igmp_stats = {} + + if "statistics" in data["igmp"]: + if stats_return: + igmp_stats["statistics"] = {} + for query, value in data["igmp"]["statistics"].items(): + if query == "queryV2": + # Verifying IGMP interface queryV2 statistics + if stats_return: + igmp_stats["statistics"][query] = intf_detail_json[ + "queryV2" + ] + + else: + if intf_detail_json["queryV2"] != value: + errormsg = ( + "[DUT %s]: IGMP interface: %s " + " queryV2 statistics verification " + "[FAILED]!! Expected : %s," + " Found : %s" + % ( + dut, + interface, + value, + intf_detail_json["queryV2"], + ) + ) + return errormsg + + logger.info( + "[DUT %s]: IGMP interface: %s " + "queryV2 statistics is %s", + dut, + interface, + value, + ) + + if query == "reportV2": + # Verifying IGMP interface timerV2 statistics + if stats_return: + igmp_stats["statistics"][query] = intf_detail_json[ + "reportV2" + ] + + else: + if intf_detail_json["reportV2"] <= value: + errormsg = ( + "[DUT %s]: IGMP reportV2 " + "statistics verification " + "[FAILED]!! Expected : %s " + "or more, Found : %s" + % ( + dut, + interface, + value, + intf_detail_json["reportV2"], + ) + ) + return errormsg + + logger.info( + "[DUT %s]: IGMP reportV2 " "statistics is %s", + dut, + intf_detail_json["reportV2"], + ) + + if "query" in data["igmp"]: + for query, value in data["igmp"]["query"].items(): + if query == "query-interval": + # Verifying IGMP interface query interval timer + if intf_detail_json["timerQueryInterval"] != value: + errormsg = ( + "[DUT %s]: IGMP interface: %s " + " query-interval verification " + "[FAILED]!! Expected : %s," + " Found : %s" + % ( + dut, + interface, + value, + intf_detail_json["timerQueryInterval"], + ) + ) + return errormsg + + logger.info( + "[DUT %s]: IGMP interface: %s " "query-interval is %s", + dut, + interface, + value, + ) + + if query == "query-max-response-time": + # Verifying IGMP interface query max response timer + if ( + intf_detail_json["timerQueryResponseIntervalMsec"] + != value * 100 + ): + errormsg = ( + "[DUT %s]: IGMP interface: %s " + "query-max-response-time " + "verification [FAILED]!!" + " Expected : %s, Found : %s" + % ( + dut, + interface, + value * 1000, + intf_detail_json["timerQueryResponseIntervalMsec"], + ) + ) + return errormsg + + logger.info( + "[DUT %s]: IGMP interface: %s " + "query-max-response-time is %s ms", + dut, + interface, + value * 100, + ) + + if query == "last-member-query-count": + # Verifying IGMP interface last member query count + if intf_detail_json["lastMemberQueryCount"] != value: + errormsg = ( + "[DUT %s]: IGMP interface: %s " + "last-member-query-count " + "verification [FAILED]!!" + " Expected : %s, Found : %s" + % ( + dut, + interface, + value, + intf_detail_json["lastMemberQueryCount"], + ) + ) + return errormsg + + logger.info( + "[DUT %s]: IGMP interface: %s " + "last-member-query-count is %s ms", + dut, + interface, + value * 1000, + ) + + if query == "last-member-query-interval": + # Verifying IGMP interface last member query interval + if ( + intf_detail_json["timerLastMemberQueryMsec"] + != value * 100 * intf_detail_json["lastMemberQueryCount"] + ): + errormsg = ( + "[DUT %s]: IGMP interface: %s " + "last-member-query-interval " + "verification [FAILED]!!" + " Expected : %s, Found : %s" + % ( + dut, + interface, + value * 1000, + intf_detail_json["timerLastMemberQueryMsec"], + ) + ) + return errormsg + + logger.info( + "[DUT %s]: IGMP interface: %s " + "last-member-query-interval is %s ms", + dut, + interface, + value * intf_detail_json["lastMemberQueryCount"] * 100, + ) + + if "version" in data["igmp"]: + # Verifying IGMP interface state is up + if intf_detail_json["state"] != "up": + errormsg = ( + "[DUT %s]: IGMP interface: %s " + " state: %s verification " + "[FAILED]!!" % (dut, interface, intf_detail_json["state"]) + ) + return errormsg + + logger.info( + "[DUT %s]: IGMP interface: %s " "state: %s", + dut, + interface, + intf_detail_json["state"], + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True if stats_return == False else igmp_stats + + +@retry(attempts=31, wait=2, return_is_str=True) +def verify_pim_config(tgen, input_dict): + """ + Verify pim interface details, verifying following configs: + drPriority + helloPeriod + helloReceived + helloSend + drAddress + + Parameters + ---------- + * `tgen`: topogen object + * `input_dict` : Input dict data, required to verify + timer + + Usage + ----- + input_dict ={ + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "pim": { + "drPriority" : 10, + "helloPeriod" : 5 + } + } + } + } + } + } + } + result = verify_pim_config(tgen, input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for dut in input_dict.keys(): + rnode = tgen.routers()[dut] + + for interface, data in input_dict[dut]["pim"]["interfaces"].items(): + + logger.info("[DUT: %s]: Verifying PIM interface %s detail:", dut, interface) + + show_ip_igmp_intf_json = run_frr_cmd( + rnode, "show ip pim interface {} json".format(interface), isjson=True + ) + + if interface not in show_ip_igmp_intf_json: + errormsg = ( + "[DUT %s]: PIM interface: %s " + " is not present in CLI output " + "[FAILED]!! " % (dut, interface) + ) + return errormsg + + intf_detail_json = show_ip_igmp_intf_json[interface] + + for config, value in data.items(): + if config == "helloPeriod": + # Verifying PIM interface helloPeriod + if intf_detail_json["helloPeriod"] != value: + errormsg = ( + "[DUT %s]: PIM interface: %s " + " helloPeriod verification " + "[FAILED]!! Expected : %s," + " Found : %s" + % (dut, interface, value, intf_detail_json["helloPeriod"]) + ) + return errormsg + + logger.info( + "[DUT %s]: PIM interface: %s " "helloPeriod is %s", + dut, + interface, + value, + ) + + if config == "drPriority": + # Verifying PIM interface drPriority + if intf_detail_json["drPriority"] != value: + errormsg = ( + "[DUT %s]: PIM interface: %s " + " drPriority verification " + "[FAILED]!! Expected : %s," + " Found : %s" + % (dut, interface, value, intf_detail_json["drPriority"]) + ) + return errormsg + + logger.info( + "[DUT %s]: PIM interface: %s " "drPriority is %s", + dut, + interface, + value, + ) + + if config == "drAddress": + # Verifying PIM interface drAddress + if intf_detail_json["drAddress"] != value: + errormsg = ( + "[DUT %s]: PIM interface: %s " + " drAddress verification " + "[FAILED]!! Expected : %s," + " Found : %s" + % (dut, interface, value, intf_detail_json["drAddress"]) + ) + return errormsg + + logger.info( + "[DUT %s]: PIM interface: %s " "drAddress is %s", + dut, + interface, + value, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=21, wait=2, return_is_dict=True) +def verify_multicast_traffic(tgen, input_dict, return_traffic=False): + """ + Verify multicast traffic by running + "show multicast traffic count json" cli + + Parameters + ---------- + * `tgen`: topogen object + * `input_dict(dict)`: defines DUT, what and for which interfaces + traffic needs to be verified + * `return_traffic`: returns traffic stats + Usage + ----- + input_dict = { + "r1": { + "traffic_received": ["r1-r0-eth0"], + "traffic_sent": ["r1-r0-eth0"] + } + } + + result = verify_multicast_traffic(tgen, input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + traffic_dict = {} + for dut in input_dict.keys(): + if dut not in tgen.routers(): + continue + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying multicast " "traffic", dut) + + show_multicast_traffic_json = run_frr_cmd( + rnode, "show ip multicast count json", isjson=True + ) + + for traffic_type, interfaces in input_dict[dut].items(): + traffic_dict[traffic_type] = {} + if traffic_type == "traffic_received": + for interface in interfaces: + traffic_dict[traffic_type][interface] = {} + interface_json = show_multicast_traffic_json[interface] + + if interface_json["pktsIn"] == 0 and interface_json["bytesIn"] == 0: + errormsg = ( + "[DUT %s]: Multicast traffic is " + "not received on interface %s " + "PktsIn: %s, BytesIn: %s " + "[FAILED]!!" + % ( + dut, + interface, + interface_json["pktsIn"], + interface_json["bytesIn"], + ) + ) + return errormsg + + elif ( + interface_json["pktsIn"] != 0 and interface_json["bytesIn"] != 0 + ): + + traffic_dict[traffic_type][interface][ + "pktsIn" + ] = interface_json["pktsIn"] + traffic_dict[traffic_type][interface][ + "bytesIn" + ] = interface_json["bytesIn"] + + logger.info( + "[DUT %s]: Multicast traffic is " + "received on interface %s " + "PktsIn: %s, BytesIn: %s " + "[PASSED]!!" + % ( + dut, + interface, + interface_json["pktsIn"], + interface_json["bytesIn"], + ) + ) + + else: + errormsg = ( + "[DUT %s]: Multicast traffic interface %s:" + " Miss-match in " + "PktsIn: %s, BytesIn: %s" + "[FAILED]!!" + % ( + dut, + interface, + interface_json["pktsIn"], + interface_json["bytesIn"], + ) + ) + return errormsg + + if traffic_type == "traffic_sent": + traffic_dict[traffic_type] = {} + for interface in interfaces: + traffic_dict[traffic_type][interface] = {} + interface_json = show_multicast_traffic_json[interface] + + if ( + interface_json["pktsOut"] == 0 + and interface_json["bytesOut"] == 0 + ): + errormsg = ( + "[DUT %s]: Multicast traffic is " + "not received on interface %s " + "PktsIn: %s, BytesIn: %s" + "[FAILED]!!" + % ( + dut, + interface, + interface_json["pktsOut"], + interface_json["bytesOut"], + ) + ) + return errormsg + + elif ( + interface_json["pktsOut"] != 0 + and interface_json["bytesOut"] != 0 + ): + + traffic_dict[traffic_type][interface][ + "pktsOut" + ] = interface_json["pktsOut"] + traffic_dict[traffic_type][interface][ + "bytesOut" + ] = interface_json["bytesOut"] + + logger.info( + "[DUT %s]: Multicast traffic is " + "received on interface %s " + "PktsOut: %s, BytesOut: %s " + "[PASSED]!!" + % ( + dut, + interface, + interface_json["pktsOut"], + interface_json["bytesOut"], + ) + ) + else: + errormsg = ( + "[DUT %s]: Multicast traffic interface %s:" + " Miss-match in " + "PktsOut: %s, BytesOut: %s " + "[FAILED]!!" + % ( + dut, + interface, + interface_json["pktsOut"], + interface_json["bytesOut"], + ) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True if return_traffic == False else traffic_dict + + +def get_refCount_for_mroute(tgen, dut, iif, src_address, group_addresses): + """ + Verify upstream inbound interface is updated correctly + by running "show ip pim upstream" cli + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + * `iif`: inbound interface + * `src_address`: source address + * `group_addresses`: IGMP group address + + Usage + ----- + dut = "r1" + iif = "r1-r0-eth0" + src_address = "*" + group_address = "225.1.1.1" + result = get_refCount_for_mroute(tgen, dut, iif, src_address, + group_address) + + Returns + ------- + refCount(int) + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + refCount = 0 + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying refCount for mroutes: ", dut) + show_ip_pim_upstream_json = run_frr_cmd( + rnode, "show ip pim upstream json", isjson=True + ) + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + for grp_addr in group_addresses: + # Verify group address + if grp_addr not in show_ip_pim_upstream_json: + errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % ( + dut, + grp_addr, + ) + return errormsg + group_addr_json = show_ip_pim_upstream_json[grp_addr] + + # Verify source address + if src_address not in group_addr_json: + errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % ( + dut, + src_address, + grp_addr, + ) + return errormsg + + # Verify Inbound Interface + if group_addr_json[src_address]["inboundInterface"] == iif: + refCount = group_addr_json[src_address]["refCount"] + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return refCount + + +@retry(attempts=21, wait=2, return_is_str=True) +def verify_multicast_flag_state(tgen, dut, src_address, group_addresses, flag): + """ + Verify flag state for mroutes and make sure (*, G)/(S, G) are having + coorect flags by running "show ip mroute" cli + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + * `src_address`: source address + * `group_addresses`: IGMP group address + * `flag`: flag state, needs to be verified + + Usage + ----- + dut = "r1" + flag = "SC" + group_address = "225.1.1.1" + result = verify_multicast_flag_state(tgen, dut, src_address, + group_address, flag) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying flag state for mroutes", dut) + show_ip_mroute_json = run_frr_cmd(rnode, "show ip mroute json", isjson=True) + + if bool(show_ip_mroute_json) == False: + error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut) + return error_msg + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + for grp_addr in group_addresses: + if grp_addr not in show_ip_mroute_json: + errormsg = ( + "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! ", + dut, + src_address, + grp_addr, + ) + return errormsg + else: + group_addr_json = show_ip_mroute_json[grp_addr] + + if src_address not in group_addr_json: + errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % ( + dut, + src_address, + grp_addr, + ) + return errormsg + else: + mroutes = group_addr_json[src_address] + + if mroutes["installed"] != 0: + logger.info( + "[DUT %s]: mroute (%s,%s) is installed", dut, src_address, grp_addr + ) + + if mroutes["flags"] != flag: + errormsg = ( + "[DUT %s]: Verifying flag for (%s, %s) " + "mroute [FAILED]!! " + "Expected: %s Found: %s" + % (dut, src_address, grp_addr, flag, mroutes["flags"]) + ) + return errormsg + + logger.info( + "[DUT %s]: Verifying flag for (%s, %s)" + " mroute, [PASSED]!! " + "Found Expected: %s", + dut, + src_address, + grp_addr, + mroutes["flags"], + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=21, wait=2, return_is_str=True) +def verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip): + """ + Verify all IGMP interface are up and running, config is verified + using "show ip igmp interface" cli + + Parameters + ---------- + * `tgen`: topogen object + * `topo` : json file data + * `dut` : device under test + * `igmp_iface` : interface name + * `interface_ip` : interface ip address + + Usage + ----- + result = verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router in tgen.routers(): + if router != dut: + continue + + logger.info("[DUT: %s]: Verifying PIM interface status:", + dut) + + rnode = tgen.routers()[dut] + show_ip_igmp_interface_json = \ + run_frr_cmd(rnode, "show ip igmp interface json", isjson=True) + + if igmp_iface in show_ip_igmp_interface_json: + igmp_intf_json = show_ip_igmp_interface_json[igmp_iface] + # Verifying igmp interface + if igmp_intf_json["address"] != interface_ip: + errormsg = ("[DUT %s]: igmp interface ip is not correct " + "[FAILED]!! Expected : %s, Found : %s" + %(dut, igmp_intf_json["address"], interface_ip)) + return errormsg + + logger.info("[DUT %s]: igmp interface: %s, " + "interface ip: %s" + " [PASSED]!!", + dut, igmp_iface, interface_ip) + else: + errormsg = ("[DUT %s]: igmp interface: %s " + "igmp interface ip: %s, is not present " + %(dut, igmp_iface, interface_ip)) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True diff --git a/tests/topotests/lib/send_bsr_packet.py b/tests/topotests/lib/send_bsr_packet.py new file mode 100755 index 0000000000..c226899324 --- /dev/null +++ b/tests/topotests/lib/send_bsr_packet.py @@ -0,0 +1,58 @@ +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +import sys +import argparse +from scapy.all import Raw, sendp +import binascii + + +def send_packet(packet, iface, interval, count): + """ + Read BSR packet in Raw format and send it to specified interface + + Parameter: + --------- + * `packet` : BSR packet in raw format + * `interface` : Interface from which packet would be send + * `interval` : Interval between the packets + * `count` : Number of packets to be sent + """ + + data = binascii.a2b_hex(packet) + p = Raw(load=data) + p.show() + sendp(p, inter=int(interval), iface=iface, count=int(count)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Send BSR Raw packet") + parser.add_argument("packet", help="Packet in raw format") + parser.add_argument("iface", help="Packet send to this ineterface") + parser.add_argument("--interval", help="Interval between packets", default=0) + parser.add_argument( + "--count", help="Number of times packet is sent repetitively", default=0 + ) + args = parser.parse_args() + + if not args.packet or not args.iface: + sys.exit(1) + + send_packet(args.packet, args.iface, args.interval, args.count) diff --git a/tests/topotests/lib/snmptest.py b/tests/topotests/lib/snmptest.py new file mode 100644 index 0000000000..5112500e0b --- /dev/null +++ b/tests/topotests/lib/snmptest.py @@ -0,0 +1,152 @@ +# +# topogen.py +# Library of helper functions for NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# +# 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. +# + +""" +SNMP library to test snmp walks and gets + +Basic usage instructions: + +* define an SnmpTester class giving a router, address, community and version +* use test_oid or test_walk to check values in MIBS +* see tests/topotest/simple-snmp-test/test_simple_snmp.py for example +""" + +from topolog import logger + + +class SnmpTester(object): + "A helper class for testing SNMP" + + def __init__(self, router, iface, community, version): + self.community = community + self.version = version + self.router = router + self.iface = iface + logger.info( + "created SNMP tester: SNMPv{0} community:{1}".format( + self.version, self.community + ) + ) + + def _snmp_config(self): + """ + Helper function to build a string with SNMP + configuration for commands. + """ + return "-v {0} -c {1} {2}".format(self.version, self.community, self.iface) + + @staticmethod + def _get_snmp_value(snmp_output): + tokens = snmp_output.strip().split() + + num_value_tokens = len(tokens) - 3 + + # this copes with the emptys string return + if num_value_tokens == 0: + return tokens[2] + + if num_value_tokens > 1: + output = "" + index = 3 + while index < len(tokens) - 1: + output += "{} ".format(tokens[index]) + index += 1 + output += "{}".format(tokens[index]) + return output + # third token is the value of the object + return tokens[3] + + @staticmethod + def _get_snmp_oid(snmp_output): + tokens = snmp_output.strip().split() + + # third token onwards is the value of the object + return tokens[0].split(".", 1)[1] + + @staticmethod + def _get_snmp_oid(snmp_output): + tokens = snmp_output.strip().split() + +# if len(tokens) > 5: +# return None + + + # third token is the value of the object + return tokens[0].split('.',1)[1] + + def _parse_multiline(self, snmp_output): + results = snmp_output.strip().split("\r\n") + + out_dict = {} + out_list = [] + for response in results: + out_dict[self._get_snmp_oid(response)] = self._get_snmp_value(response) + out_list.append(self._get_snmp_value(response)) + + return out_dict, out_list + + def get(self, oid): + cmd = "snmpget {0} {1}".format(self._snmp_config(), oid) + + result = self.router.cmd(cmd) + if "not found" in result: + return None + return self._get_snmp_value(result) + + def get_next(self, oid): + cmd = "snmpgetnext {0} {1}".format(self._snmp_config(), oid) + + result = self.router.cmd(cmd) + print("get_next: {}".format(result)) + if "not found" in result: + return None + return self._get_snmp_value(result) + + def walk(self, oid): + cmd = "snmpwalk {0} {1}".format(self._snmp_config(), oid) + + result = self.router.cmd(cmd) + return self._parse_multiline(result) + + def test_oid(self, oid, value): + print("oid: {}".format(self.get_next(oid))) + return self.get_next(oid) == value + + def test_oid_walk(self, oid, values, oids=None): + results_dict, results_list = self.walk(oid) + print("test_oid_walk: {} {}".format(oid, results_dict)) + if oids is not None: + index = 0 + for oid in oids: + # avoid key error for missing keys + if not oid in results_dict.keys(): + print("FAIL: missing oid key {}".format(oid)) + return False + if results_dict[oid] != values[index]: + print("FAIL{} {} |{}| == |{}|".format(oid, index, results_dict[oid], values[index])) + return False + index += 1 + return True + + # Return true if 'values' is a subset of 'results_list' + print("test {} == {}".format(results_list[: len(values)], values)) + return results_list[: len(values)] == values diff --git a/tests/topotests/lib/test/test_json.py b/tests/topotests/lib/test/test_json.py index b85e193d3b..7b3c8593cc 100755 --- a/tests/topotests/lib/test/test_json.py +++ b/tests/topotests/lib/test/test_json.py @@ -107,16 +107,25 @@ def test_json_intersect_multilevel_true(): dcomplete = { "i1": "item1", "i2": "item2", - "i3": {"i100": "item100",}, + "i3": { + "i100": "item100", + }, "i4": { - "i41": {"i411": "item411",}, - "i42": {"i421": "item421", "i422": "item422",}, + "i41": { + "i411": "item411", + }, + "i42": { + "i421": "item421", + "i422": "item422", + }, }, } dsub1 = { "i1": "item1", - "i3": {"i100": "item100",}, + "i3": { + "i100": "item100", + }, "i10": None, } dsub2 = { @@ -126,10 +135,36 @@ def test_json_intersect_multilevel_true(): } dsub3 = { "i2": "item2", - "i4": {"i41": {"i411": "item411",}, "i42": {"i422": "item422", "i450": None,}}, + "i4": { + "i41": { + "i411": "item411", + }, + "i42": { + "i422": "item422", + "i450": None, + }, + }, + } + dsub4 = { + "i2": "item2", + "i4": { + "i41": {}, + "i42": { + "i450": None, + }, + }, + } + dsub5 = { + "i2": "item2", + "i3": { + "i100": "item100", + }, + "i4": { + "i42": { + "i450": None, + } + }, } - dsub4 = {"i2": "item2", "i4": {"i41": {}, "i42": {"i450": None,}}} - dsub5 = {"i2": "item2", "i3": {"i100": "item100",}, "i4": {"i42": {"i450": None,}}} assert json_cmp(dcomplete, dsub1) is None assert json_cmp(dcomplete, dsub2) is None @@ -144,17 +179,26 @@ def test_json_intersect_multilevel_false(): dcomplete = { "i1": "item1", "i2": "item2", - "i3": {"i100": "item100",}, + "i3": { + "i100": "item100", + }, "i4": { - "i41": {"i411": "item411",}, - "i42": {"i421": "item421", "i422": "item422",}, + "i41": { + "i411": "item411", + }, + "i42": { + "i421": "item421", + "i422": "item422", + }, }, } # Incorrect sub-level value dsub1 = { "i1": "item1", - "i3": {"i100": "item00",}, + "i3": { + "i100": "item00", + }, "i10": None, } # Inexistent sub-level @@ -166,14 +210,41 @@ def test_json_intersect_multilevel_false(): # Inexistent sub-level value dsub3 = { "i1": "item1", - "i3": {"i100": None,}, + "i3": { + "i100": None, + }, } # Inexistent sub-sub-level value - dsub4 = {"i4": {"i41": {"i412": "item412",}, "i42": {"i421": "item421",}}} + dsub4 = { + "i4": { + "i41": { + "i412": "item412", + }, + "i42": { + "i421": "item421", + }, + } + } # Invalid sub-sub-level value - dsub5 = {"i4": {"i41": {"i411": "item411",}, "i42": {"i421": "item420000",}}} + dsub5 = { + "i4": { + "i41": { + "i411": "item411", + }, + "i42": { + "i421": "item420000", + }, + } + } # sub-sub-level should be value - dsub6 = {"i4": {"i41": {"i411": "item411",}, "i42": "foobar",}} + dsub6 = { + "i4": { + "i41": { + "i411": "item411", + }, + "i42": "foobar", + } + } assert json_cmp(dcomplete, dsub1) is not None assert json_cmp(dcomplete, dsub2) is not None @@ -187,7 +258,15 @@ def test_json_with_list_sucess(): "Test successful json comparisons that have lists." dcomplete = { - "list": [{"i1": "item 1", "i2": "item 2",}, {"i10": "item 10",},], + "list": [ + { + "i1": "item 1", + "i2": "item 2", + }, + { + "i10": "item 10", + }, + ], "i100": "item 100", } @@ -197,12 +276,19 @@ def test_json_with_list_sucess(): } # Test list correct list items dsub2 = { - "list": [{"i1": "item 1",},], + "list": [ + { + "i1": "item 1", + }, + ], "i100": "item 100", } # Test list correct list size dsub3 = { - "list": [{}, {},], + "list": [ + {}, + {}, + ], } assert json_cmp(dcomplete, dsub1) is None @@ -214,7 +300,15 @@ def test_json_with_list_failure(): "Test failed json comparisons that have lists." dcomplete = { - "list": [{"i1": "item 1", "i2": "item 2",}, {"i10": "item 10",},], + "list": [ + { + "i1": "item 1", + "i2": "item 2", + }, + { + "i10": "item 10", + }, + ], "i100": "item 100", } @@ -224,12 +318,20 @@ def test_json_with_list_failure(): } # Test list incorrect list items dsub2 = { - "list": [{"i1": "item 2",},], + "list": [ + { + "i1": "item 2", + }, + ], "i100": "item 100", } # Test list correct list size dsub3 = { - "list": [{}, {}, {},], + "list": [ + {}, + {}, + {}, + ], } assert json_cmp(dcomplete, dsub1) is not None @@ -241,20 +343,52 @@ def test_json_list_start_success(): "Test JSON encoded data that starts with a list that should succeed." dcomplete = [ - {"id": 100, "value": "abc",}, - {"id": 200, "value": "abcd",}, - {"id": 300, "value": "abcde",}, + { + "id": 100, + "value": "abc", + }, + { + "id": 200, + "value": "abcd", + }, + { + "id": 300, + "value": "abcde", + }, ] - dsub1 = [{"id": 100, "value": "abc",}] + dsub1 = [ + { + "id": 100, + "value": "abc", + } + ] - dsub2 = [{"id": 100, "value": "abc",}, {"id": 200, "value": "abcd",}] + dsub2 = [ + { + "id": 100, + "value": "abc", + }, + { + "id": 200, + "value": "abcd", + }, + ] - dsub3 = [{"id": 300, "value": "abcde",}] + dsub3 = [ + { + "id": 300, + "value": "abcde", + } + ] dsub4 = [] - dsub5 = [{"id": 100,}] + dsub5 = [ + { + "id": 100, + } + ] assert json_cmp(dcomplete, dsub1) is None assert json_cmp(dcomplete, dsub2) is None @@ -272,13 +406,44 @@ def test_json_list_start_failure(): {"id": 300, "value": "abcde"}, ] - dsub1 = [{"id": 100, "value": "abcd",}] + dsub1 = [ + { + "id": 100, + "value": "abcd", + } + ] - dsub2 = [{"id": 100, "value": "abc",}, {"id": 200, "value": "abc",}] + dsub2 = [ + { + "id": 100, + "value": "abc", + }, + { + "id": 200, + "value": "abc", + }, + ] - dsub3 = [{"id": 100, "value": "abc",}, {"id": 350, "value": "abcde",}] + dsub3 = [ + { + "id": 100, + "value": "abc", + }, + { + "id": 350, + "value": "abcde", + }, + ] - dsub4 = [{"value": "abcx",}, {"id": 300, "value": "abcde",}] + dsub4 = [ + { + "value": "abcx", + }, + { + "id": 300, + "value": "abcde", + }, + ] assert json_cmp(dcomplete, dsub1) is not None assert json_cmp(dcomplete, dsub2) is not None diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index eaf7f90479..f958cc11d3 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -336,7 +336,9 @@ class Topogen(object): for gear in self.gears.values(): errors += gear.stop() if len(errors) > 0: - logger.error("Errors found post shutdown - details follow: {}".format(errors)) + logger.error( + "Errors found post shutdown - details follow: {}".format(errors) + ) self.net.stop() @@ -552,6 +554,8 @@ class TopoRouter(TopoGear): RD_SHARP = 14 RD_BABEL = 15 RD_PBRD = 16 + RD_PATH = 17 + RD_SNMP = 18 RD = { RD_ZEBRA: "zebra", RD_RIP: "ripd", @@ -569,6 +573,8 @@ class TopoRouter(TopoGear): RD_SHARP: "sharpd", RD_BABEL: "babeld", RD_PBRD: "pbrd", + RD_PATH: "pathd", + RD_SNMP: "snmpd", } def __init__(self, tgen, cls, name, **params): @@ -653,7 +659,7 @@ class TopoRouter(TopoGear): Possible daemon values are: TopoRouter.RD_ZEBRA, TopoRouter.RD_RIP, TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6, TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP, - TopoRouter.RD_PIM, TopoRouter.RD_PBR. + TopoRouter.RD_PIM, TopoRouter.RD_PBR, TopoRouter.RD_SNMP. """ daemonstr = self.RD.get(daemon) self.logger.info('loading "{}" configuration: {}'.format(daemonstr, source)) diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py index f2fafa5e2a..fcc6c19868 100644 --- a/tests/topotests/lib/topojson.py +++ b/tests/topotests/lib/topojson.py @@ -43,8 +43,9 @@ from lib.common_config import ( create_vrf_cfg, ) +from lib.pim import create_pim_config, create_igmp_config from lib.bgp import create_router_bgp -from lib.ospf import create_router_ospf +from lib.ospf import create_router_ospf, create_router_ospf6 ROUTER_LIST = [] @@ -68,20 +69,18 @@ def build_topo_from_json(tgen, topo): topo["switches"].keys(), key=lambda x: int(re_search("\d+", x).group(0)) ) - listRouters = ROUTER_LIST[:] - listSwitches = SWITCH_LIST[:] + listRouters = sorted(ROUTER_LIST[:]) + listSwitches = sorted(SWITCH_LIST[:]) listAllRouters = deepcopy(listRouters) dictSwitches = {} for routerN in ROUTER_LIST: logger.info("Topo: Add router {}".format(routerN)) tgen.add_router(routerN) - listRouters.append(routerN) for switchN in SWITCH_LIST: logger.info("Topo: Add switch {}".format(switchN)) dictSwitches[switchN] = tgen.add_switch(switchN) - listSwitches.append(switchN) if "ipv4base" in topo: ipv4Next = ipaddress.IPv4Address(topo["link_ip_start"]["ipv4"]) @@ -96,33 +95,25 @@ def build_topo_from_json(tgen, topo): for router in listRouters: topo["routers"][router]["nextIfname"] = 0 + router_count = 0 while listRouters != []: curRouter = listRouters.pop(0) # Physical Interfaces if "links" in topo["routers"][curRouter]: - - def link_sort(x): - if x == "lo": - return 0 - elif "link" in x: - return int(x.split("-link")[1]) - else: - return int(re_search("\d+", x).group(0)) - for destRouterLink, data in sorted( - topo["routers"][curRouter]["links"].items(), - key=lambda x: link_sort(x[0]), + topo["routers"][curRouter]["links"].iteritems() ): currRouter_lo_json = topo["routers"][curRouter]["links"][destRouterLink] # Loopback interfaces if "type" in data and data["type"] == "loopback": + router_count += 1 if ( "ipv4" in currRouter_lo_json and currRouter_lo_json["ipv4"] == "auto" ): currRouter_lo_json["ipv4"] = "{}{}.{}/{}".format( topo["lo_prefix"]["ipv4"], - number_to_row(curRouter), + router_count, number_to_column(curRouter), topo["lo_prefix"]["v4mask"], ) @@ -132,7 +123,7 @@ def build_topo_from_json(tgen, topo): ): currRouter_lo_json["ipv6"] = "{}{}:{}/{}".format( topo["lo_prefix"]["ipv6"], - number_to_row(curRouter), + router_count, number_to_column(curRouter), topo["lo_prefix"]["v6mask"], ) @@ -167,6 +158,14 @@ def build_topo_from_json(tgen, topo): destRouter, curRouter, topo["routers"][destRouter]["nextIfname"] ) + # add link interface + destRouter_link_json["peer-interface"] = "{}-{}-eth{}".format( + curRouter, destRouter, topo["routers"][curRouter]["nextIfname"] + ) + currRouter_link_json["peer-interface"] = "{}-{}-eth{}".format( + destRouter, curRouter, topo["routers"][destRouter]["nextIfname"] + ) + topo["routers"][curRouter]["nextIfname"] += 1 topo["routers"][destRouter]["nextIfname"] += 1 @@ -311,8 +310,11 @@ def build_config_from_json(tgen, topo, save_bkup=True): ("prefix_lists", create_prefix_lists), ("bgp_community_list", create_bgp_community_lists), ("route_maps", create_route_maps), + ("pim", create_pim_config), + ("igmp", create_igmp_config), ("bgp", create_router_bgp), ("ospf", create_router_ospf), + ("ospf6", create_router_ospf6), ] ) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 20d60ebbef..1e6ef1b2b3 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -609,8 +609,10 @@ def interface_set_status(node, ifacename, ifaceaction=False, vrf_name=None): ifacename, str_ifaceaction ) else: - cmd = 'vtysh -c "configure terminal" -c "interface {0} vrf {1}" -c "{2}"'.format( - ifacename, vrf_name, str_ifaceaction + cmd = ( + 'vtysh -c "configure terminal" -c "interface {0} vrf {1}" -c "{2}"'.format( + ifacename, vrf_name, str_ifaceaction + ) ) node.run(cmd) @@ -924,40 +926,44 @@ def checkAddressSanitizerError(output, router, component, logdir=""): ) if addressSanitizerLog: # Find Calling Test. Could be multiple steps back - testframe=sys._current_frames().values()[0] - level=0 + testframe = sys._current_frames().values()[0] + level = 0 while level < 10: - test=os.path.splitext(os.path.basename(testframe.f_globals["__file__"]))[0] + test = os.path.splitext( + os.path.basename(testframe.f_globals["__file__"]) + )[0] if (test != "topotest") and (test != "topogen"): # Found the calling test - callingTest=os.path.basename(testframe.f_globals["__file__"]) + callingTest = os.path.basename(testframe.f_globals["__file__"]) break - level=level+1 - testframe=testframe.f_back - if (level >= 10): + level = level + 1 + testframe = testframe.f_back + if level >= 10: # somehow couldn't find the test script. - callingTest="unknownTest" + callingTest = "unknownTest" # # Now finding Calling Procedure - level=0 + level = 0 while level < 20: - callingProc=sys._getframe(level).f_code.co_name - if ((callingProc != "processAddressSanitizerError") and - (callingProc != "checkAddressSanitizerError") and - (callingProc != "checkRouterCores") and - (callingProc != "stopRouter") and - (callingProc != "__stop_internal") and - (callingProc != "stop") and - (callingProc != "stop_topology") and - (callingProc != "checkRouterRunning") and - (callingProc != "check_router_running") and - (callingProc != "routers_have_failure")): + callingProc = sys._getframe(level).f_code.co_name + if ( + (callingProc != "processAddressSanitizerError") + and (callingProc != "checkAddressSanitizerError") + and (callingProc != "checkRouterCores") + and (callingProc != "stopRouter") + and (callingProc != "__stop_internal") + and (callingProc != "stop") + and (callingProc != "stop_topology") + and (callingProc != "checkRouterRunning") + and (callingProc != "check_router_running") + and (callingProc != "routers_have_failure") + ): # Found the calling test break - level=level+1 - if (level >= 20): + level = level + 1 + if level >= 20: # something wrong - couldn't found the calling test function - callingProc="unknownProc" + callingProc = "unknownProc" with open("/tmp/AddressSanitzer.txt", "a") as addrSanFile: sys.stderr.write( "AddressSanitizer error in topotest `%s`, test `%s`, router `%s`\n\n" @@ -979,7 +985,6 @@ def checkAddressSanitizerError(output, router, component, logdir=""): addrSanFile.write("\n---------------\n") return - addressSanitizerError = re.search( "(==[0-9]+==)ERROR: AddressSanitizer: ([^\s]*) ", output ) @@ -989,16 +994,20 @@ def checkAddressSanitizerError(output, router, component, logdir=""): # No Address Sanitizer Error in Output. Now check for AddressSanitizer daemon file if logdir: - filepattern=logdir+"/"+router+"/"+component+".asan.*" - logger.debug("Log check for %s on %s, pattern %s\n" % (component, router, filepattern)) + filepattern = logdir + "/" + router + "/" + component + ".asan.*" + logger.debug( + "Log check for %s on %s, pattern %s\n" % (component, router, filepattern) + ) for file in glob.glob(filepattern): with open(file, "r") as asanErrorFile: - asanError=asanErrorFile.read() + asanError = asanErrorFile.read() addressSanitizerError = re.search( "(==[0-9]+==)ERROR: AddressSanitizer: ([^\s]*) ", asanError - ) + ) if addressSanitizerError: - processAddressSanitizerError(addressSanitizerError, asanError, router, component) + processAddressSanitizerError( + addressSanitizerError, asanError, router, component + ) return True return False @@ -1065,7 +1074,7 @@ class Router(Node): if self.logdir is None: cur_test = os.environ["PYTEST_CURRENT_TEST"] self.logdir = "/tmp/topotests/" + cur_test[ - cur_test.find("/")+1 : cur_test.find(".py") + cur_test.find("/") + 1 : cur_test.find(".py") ].replace("/", ".") # If the logdir is not created, then create it and set the @@ -1073,7 +1082,7 @@ class Router(Node): if not os.path.isdir(self.logdir): os.system("mkdir -p " + self.logdir + "/" + name) os.system("chmod -R go+rw /tmp/topotests") - # Erase logs of previous run + # Erase logs of previous run os.system("rm -rf " + self.logdir + "/" + name) self.daemondir = None @@ -1096,6 +1105,8 @@ class Router(Node): "sharpd": 0, "babeld": 0, "pbrd": 0, + "pathd": 0, + "snmpd": 0, } self.daemons_options = {"zebra": ""} self.reportCores = True @@ -1279,6 +1290,8 @@ class Router(Node): % (self.routertype, self.routertype, self.routertype, daemon) ) self.waitOutput() + if (daemon == "snmpd") and (self.routertype == "frr"): + self.cmd('echo "agentXSocket /etc/frr/agentx" > /etc/snmp/frr.conf') if (daemon == "zebra") and (self.daemons["staticd"] == 0): # Add staticd with zebra - if it exists staticd_path = os.path.join(self.daemondir, "staticd") @@ -1435,6 +1448,20 @@ class Router(Node): while "staticd" in daemons_list: daemons_list.remove("staticd") + if "snmpd" in daemons_list: + snmpd_path = "/usr/sbin/snmpd" + snmpd_option = self.daemons_options["snmpd"] + self.cmd( + "{0} {1} -C -c /etc/frr/snmpd.conf -p /var/run/{2}/snmpd.pid -x /etc/frr/agentx > snmpd.out 2> snmpd.err".format( + snmpd_path, snmpd_option, self.routertype + ) + ) + logger.info("{}: {} snmpd started".format(self, self.routertype)) + + # Remove `snmpd` so we don't attempt to start it again. + while "snmpd" in daemons_list: + daemons_list.remove("snmpd") + # Fix Link-Local Addresses # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this self.cmd( @@ -1598,6 +1625,8 @@ class Router(Node): return "%s: vtysh killed by AddressSanitizer" % (self.name) for daemon in self.daemons: + if daemon == "snmpd": + continue if (self.daemons[daemon] == 1) and not (daemon in daemonsRunning): sys.stderr.write("%s: Daemon %s not running\n" % (self.name, daemon)) if daemon == "staticd": diff --git a/tests/topotests/multicast-pim-bsm-topo1/mcast_pim_bsmp_01.json b/tests/topotests/multicast-pim-bsm-topo1/mcast_pim_bsmp_01.json new file mode 100644 index 0000000000..14cb0bee1d --- /dev/null +++ b/tests/topotests/multicast-pim-bsm-topo1/mcast_pim_bsmp_01.json @@ -0,0 +1,238 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "routers": { + "b1": { + "links": { + "f1": {"ipv4": "auto", "pim": "enable"}, + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"} + }, + "bsm": { + "bsr_packets": { + "packet1" : { + "data": "01005e00000d005056961165080045c000aa5af500000167372a46000001e000000d2400f5ce165b000001004600000101000018e1010100080800000100090a090a0096650001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e1010101010100000100050606050096000001000020e20101010101000001000909090900960000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"], + "225.1.1.1/32": ["5.6.6.5/32"], + "225.200.100.100/32": ["210.210.210.210/32"], + "226.1.1.1/32": ["9.9.9.9/32"] + + }, + "Desc" : "Packet with 3 group range - rp prio different" + }, + "packet2" : { + "data": "01005e00000d005056961165080045c0009420f400000167714146000001e000000d24000b3b164a000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e20101010101000001000909090900000000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"] + }, + "Desc" : "Packet 1 with hold time 0 for 226.1.1.1/32" + }, + "packet3" : { + "data": "01005e00000d005056961165080045c000944d0000000167453546000001e000000d2400e52b17c3000001004600000101000018e1010100080800000100090a090a0096650001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e20101010101000001000909090900960000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"], + "226.1.1.1/32": ["9.9.9.9/32"] + }, + "Desc" : "BSR Prio - TC 4" + }, + "packet4" : { + "data": "01005e00000d005056961165080045c000aa3d1c00000167550346000001e000000d24000d671c52000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e1010101010100000100090909090000000001000020e20101010101000001000909090900960000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"], + "225.1.1.1/32": ["9.9.9.9/32"], + "226.1.1.1/32": ["9.9.9.9/32"] + }, + "Desc" : "TC - 5" + }, + "packet5" : { + "data": "01005e00000d005056961165080045c000aa3d1c00000167550346000001e000000d24000d671c52000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e1010101010100000100090909090000000001000020e20101010101000001000909090900960000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"], + "226.1.1.1/32": ["9.9.9.9/32"] + }, + "Desc" : "TC - 5, 225.1.1.1 with hold time 0" + }, + "packet6" : { + "data": "01005e00000d005056961165080045c0008a795e0000016718e146000001e000000d24006cc509d5000001004600000101000018e10101000707000001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e20101010101000001000909090900960000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"], + "226.1.1.1/32": ["9.9.9.9/32"] + }, + "Desc" : "TC - 6,High prio rp removed on 225.1.1.0/24" + }, + "packet7" : { + "data": "01005e00000d005056961165080045c0007e6ebb00000167239046000001e000000d2400090810b3000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a00966400", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"] + }, + "Desc" : "TC - 8, rps with same priority" + }, + + "packet8" : { + "data": "01005e00000d005056b76687080045c000383cdf0000016755b246000001e000000d24008ad51a9f000001004600000101000020e1c86464010100000100d2d2d2d200960000", + "group": "225.200.100.100/32", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.200.100.100/32": ["210.210.210.210/32"] + }, + "Desc" : "TC - 30, grp add with all octet" + }, + + "packet9" : { + "data": "01005e00000d005056b76687080045c000387b8600000167170b46000001e000000d2400c6282245000001000101020701000020e1c86464010100000100d2d2d2d200960000", + "group": "225.200.100.100/32", + "candidate_rp": "210.210.210.210/32", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "1.1.2.7/32", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.200.100.100/32": ["210.210.210.210/32"] + }, + "Desc" : "TC -29, BSM with preferred ip" + } + + } + } + }, + + "b2": { + "links": { + "f1": {"ipv4": "auto", "pim": "enable"}, + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"} + }, + "bsm": { + "bsr_packets": { + "packet1" : { + "data": "01005e00000d005056b70489080045c0003865db0000016731b641000001e000000d2400659c0c6f000001004100000101000018e10101000101000001002121212100960000", + "src_ip": "65.0.0.1/24", + "dest_ip": "65.0.0.2/24", + "bsr": "65.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["33.33.33.33/32"], + "225.200.100.100/32": ["210.210.210.210/32"] + } + }, + "packet2" : { + "data": "01005e00000d005056b70489080045c00038663000000167316141000001e000000d24006dce0433000a01004100000101000018e10101000101000001002121212100960000", + "src_ip": "65.0.0.1/24", + "dest_ip": "65.0.0.2/24", + "bsr": "65.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["33.33.33.33/32"] + } + }, + + "packet3" : { + "data": "01005e00000d005056b76687080045c00038f5c800000167a1c841000001e000000d2400c6621a10000001000a02010101000020e1c86464010100000100d2d2d2d200960000", + "src_ip": "65.0.0.1/24", + "dest_ip": "65.0.0.2/24", + "bsr": "10.2.1.1/32", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.200.100.100/32": ["210.210.210.210/32"] + } + } + + } + } + }, + + "f1": { + "links": { + "b1": {"ipv4": "auto", "pim": "enable"}, + "b2": {"ipv4": "auto", "pim": "enable"}, + "i1": {"ipv4": "auto", "pim": "enable"}, + "s1": {"ipv4": "auto", "pim": "enable"} + } + }, + "i1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"} + } + }, + "l1": { + "links": { + "i1": {"ipv4": "auto", "pim": "enable"}, + "r1": {"ipv4": "auto", "pim": "enable"} + }, + "igmp": { + "interfaces": { + "l1-r1-eth1" :{ + "igmp":{ + "version": "2" + } + } + } + } + }, + "s1": { + "links": { + "f1": {"ipv4": "auto", "pim": "enable"} + } + }, + "r1": { + "links": { + "l1": {"ipv4": "auto", "pim": "disable"} + } + } + } +} diff --git a/tests/topotests/multicast-pim-bsm-topo1/test_mcast_pim_bsmp_01.py b/tests/topotests/multicast-pim-bsm-topo1/test_mcast_pim_bsmp_01.py new file mode 100644 index 0000000000..c670c82d21 --- /dev/null +++ b/tests/topotests/multicast-pim-bsm-topo1/test_mcast_pim_bsmp_01.py @@ -0,0 +1,1653 @@ +#!/usr/bin/env python +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +""" +Following tests are covered to test PIM BSM processing basic functionality: + +Test steps +- Create topology (setup module) +- Bring up topology + +Tests covered in this suite +1. Verify FRR router select higher IP BSR , when 2 BSR present in the network +2. Verify BSR and RP updated correctly after configuring as black hole address +3.1 Verify when new router added to the topology, FRR node will send + unicast BSM to new router +3.2 Verify if no forwarding bit is set , FRR is not forwarding the + BSM to other PIM nbrs +3.3 Verify multicast BSM is sent to new router when unicast BSM is disabled +4.1 Verfiy BSM arrived on non bsm capable interface is dropped and + not processed +4.2 Verify group to RP info updated correctly in FRR node, after shut and + no-shut of BSM enable interfaces +5. Verify static RP is preferred over BSR +6.1 Verify adding/deleting the group to rp mapping and RP priority + multiple times +6.2 Verify RP and (*,G) detail after PIM process restart on FRR node +7.1 Verify BSM timeout on FRR1 +7.2 Verify RP state in FRR1 after Bootstrap timer expiry +8.1 Verify upstream interfaces(IIF) and join state are updated properly + after BSM received for FRR +8.2 Verify IIF and OIL in "show ip pim state" updated properly after + BSM received +""" + +import os +import sys +import json +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + addKernelRoute, + create_static_routes, + iperfSendIGMPJoin, + stop_router, + start_router, + shutdown_bringup_interface, + kill_router_daemons, + start_router_daemons, + reset_config_on_routers, + do_countdown, + apply_raw_config, + kill_iperf, + run_frr_cmd, + required_linux_kernel_version, + topo_daemons, +) + +from lib.pim import ( + create_pim_config, + add_rp_interfaces_and_pim_config, + reconfig_interfaces, + scapy_send_bsr_raw_packet, + find_rp_from_bsrp_info, + verify_pim_grp_rp_source, + verify_pim_bsr, + verify_ip_mroutes, + verify_join_state_and_timer, + verify_pim_state, + verify_upstream_iif, + verify_igmp_groups, + verify_ip_pim_upstream_rpf, + enable_disable_pim_unicast_bsm, + enable_disable_pim_bsm, + clear_ip_mroute, + clear_ip_pim_interface_traffic, + verify_pim_interface_traffic, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/mcast_pim_bsmp_01.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +TOPOLOGY = """ + + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + +""" +# Global variables +NEXT_HOP1 = "70.0.0.1" +NEXT_HOP2 = "65.0.0.1" +BSR_IP_1 = "1.1.2.7" +BSR_IP_2 = "10.2.1.1" +BSR1_ADDR = "1.1.2.7/32" +BSR2_ADDR = "10.2.1.1/32" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Local APIs +# +##################################################### + + +def clear_bsrp_data(tgen, topo): + + """ + clear bsm databas after test" + Parameters + ---------- + * `tgen`: topogen object + + Usage + ----- + result = clear_bsrp_data(tgen, topo) + Returns + ------- + errormsg(str) or True + """ + + for dut in tgen.routers(): + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: clear_bsrp_data") + + run_frr_cmd(rnode, "clear ip pim bsr-data") + + return True + + +def verify_state_incremented(state_before, state_after): + """ + API to compare interface traffic state incrementing + + Parameters + ---------- + * `state_before` : State dictionary for any particular instance + * `state_after` : State dictionary for any particular instance + """ + + for router, state_data in state_before.items(): + for state, value in state_data.items(): + if state_before[router][state] >= state_after[router][state]: + errormsg = ( + "[DUT: %s]: state %s value has not" + " incremented, Initial value: %s, " + "Current value: %s [FAILED!!]" + % ( + router, + state, + state_before[router][state], + state_after[router][state], + ) + ) + return errormsg + + logger.info( + "[DUT: %s]: State %s value is " + "incremented, Initial value: %s, Current value: %s" + " [PASSED!!]", + router, + state, + state_before[router][state], + state_after[router][state], + ) + + return True + + +def pre_config_to_bsm(tgen, topo, tc_name, bsr, sender, receiver, fhr, rp, lhr, packet): + """ + API to do required configuration to send and receive BSR packet + """ + + # Re-configure interfaces as per BSR packet + result = reconfig_interfaces(tgen, topo, bsr, fhr, packet) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Create static routes + if "bsr" in topo["routers"][bsr]["bsm"]["bsr_packets"][packet]: + bsr_route = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["bsr"] + next_hop = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["src_ip"].split( + "/" + )[0] + next_hop_rp = topo["routers"][fhr]["links"][rp]["ipv4"].split("/")[0] + next_hop_lhr = topo["routers"][rp]["links"][lhr]["ipv4"].split("/")[0] + + # Add static routes + input_dict = { + fhr: {"static_routes": [{"network": bsr_route, "next_hop": next_hop}]}, + rp: {"static_routes": [{"network": bsr_route, "next_hop": next_hop_rp}]}, + lhr: {"static_routes": [{"network": bsr_route, "next_hop": next_hop_lhr}]}, + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Add kernal route for source + group = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["pkt_dst"] + bsr_interface = topo["routers"][bsr]["links"][fhr]["interface"] + result = addKernelRoute(tgen, bsr, bsr_interface, group) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # RP Mapping + rp_mapping = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["rp_mapping"] + + # Add interfaces in RP for all the RPs + result = add_rp_interfaces_and_pim_config(tgen, topo, "lo", rp, rp_mapping) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Add kernal routes to sender and receiver + for group, rp_list in rp_mapping.items(): + mask = group.split("/")[1] + if int(mask) == 32: + group = group.split("/")[0] + + # Add kernal routes for sender + s_interface = topo["routers"][sender]["links"][fhr]["interface"] + result = addKernelRoute(tgen, sender, s_interface, group) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Add kernal routes for receiver + r_interface = topo["routers"][receiver]["links"][lhr]["interface"] + result = addKernelRoute(tgen, receiver, r_interface, group) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Add static routes for RPs in FHR and LHR + next_hop_fhr = topo["routers"][rp]["links"][fhr]["ipv4"].split("/")[0] + next_hop_lhr = topo["routers"][rp]["links"][lhr]["ipv4"].split("/")[0] + input_dict = { + fhr: {"static_routes": [{"network": rp_list, "next_hop": next_hop_fhr}]}, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + input_dict = { + lhr: {"static_routes": [{"network": rp_list, "next_hop": next_hop_lhr}]}, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + return True + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_BSR_higher_prefer_ip_p0(request): + """ + Verify FRR router select higher IP BSR , when 2 BSR present in the network + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + step("pre-configure BSM packet") + step("Configure cisco-1 as BSR1 1.1.2.7") + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + step("Configure cisco-1 as BSR1 10.2.1.1") + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + step("configuring loopback address of b1 and b2 as BSR") + intf_lo_addr_b1 = topo["routers"]["b1"]["links"]["lo"]["ipv4"] + intf_lo_addr_b2 = topo["routers"]["b2"]["links"]["lo"]["ipv4"] + + raw_config = { + "b1": { + "raw_config": [ + "interface lo", + "no ip address {}".format(intf_lo_addr_b1), + "ip address {}".format(BSR1_ADDR), + "ip pim", + ] + }, + "b2": { + "raw_config": [ + "interface lo", + "no ip address {}".format(intf_lo_addr_b2), + "ip address {}".format(BSR2_ADDR), + "ip pim", + ] + }, + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + GROUP_ADDRESS = "225.200.100.100" + step("configuring static routes for both the BSR") + + next_hop_rp = topo["routers"]["f1"]["links"]["i1"]["ipv4"].split("/")[0] + next_hop_lhr = topo["routers"]["i1"]["links"]["l1"]["ipv4"].split("/")[0] + + input_dict = { + "f1": { + "static_routes": [ + {"network": BSR1_ADDR, "next_hop": NEXT_HOP1}, + {"network": BSR2_ADDR, "next_hop": NEXT_HOP2}, + ] + }, + "i1": { + "static_routes": [ + {"network": BSR1_ADDR, "next_hop": next_hop_rp}, + {"network": BSR2_ADDR, "next_hop": next_hop_rp}, + ] + }, + "l1": { + "static_routes": [ + {"network": BSR1_ADDR, "next_hop": next_hop_lhr}, + {"network": BSR2_ADDR, "next_hop": next_hop_lhr}, + ] + }, + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Use scapy to send pre-defined packet from senser to receiver + step("Send BSR packet from b1 to FHR") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet9") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + do_countdown(5) + + dut = "l1" + step("Verify if b1 chosen as BSR in f1") + result = verify_pim_bsr(tgen, topo, "f1", BSR_IP_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + group = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["group"] + step("Find the elected rp from bsrp-info in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_1, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify RP in LHR") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("Send BSR packet from b2 to FHR") + result = scapy_send_bsr_raw_packet(tgen, topo, "b2", "f1", "packet3") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "l1" + step("Verify if b2 chosen as BSR in f1") + result = verify_pim_bsr(tgen, topo, "f1", BSR_IP_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Find the elected rp from bsrp-info in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_2, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify RP in LHR") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("Shut higher prefer BSR2 link f1 to b2") + + f1_b2_eth1 = topo["routers"]["f1"]["links"]["b2"]["interface"] + shutdown_bringup_interface(tgen, "f1", "f1-b2-eth1", False) + + step("clearing bsr to timeout old BSR") + clear_bsrp_data(tgen, topo) + + step("Send BSR packet from b1 and b2 to FHR") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet9") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = scapy_send_bsr_raw_packet(tgen, topo, "b2", "f1", "packet3") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("sleeping for 3 sec to leran new packet") + do_countdown(3) + step("verify BSR1 is become prefered RP") + dut = "l1" + + step("Verify if b1 chosen as BSR in f1") + result = verify_pim_bsr(tgen, topo, "f1", BSR_IP_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Find the elected rp from bsrp-info in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_1, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify RP in LHR") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("NoShut higher prefer BSR2 link f1 to b2") + step("sleeping for 3 min to leran new packet") + do_countdown(3) + f1_b2_eth1 = topo["routers"]["f1"]["links"]["b2"]["interface"] + shutdown_bringup_interface(tgen, "f1", "f1-b2-eth1", True) + step("verify BSR2 is become prefered RP") + dut = "l1" + + step("Send BSR packet from b1 and b2 to FHR") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet9") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = scapy_send_bsr_raw_packet(tgen, topo, "b2", "f1", "packet3") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify if b2 chosen as BSR in f1") + result = verify_pim_bsr(tgen, topo, "f1", BSR_IP_2) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Find the elected rp from bsrp-info in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_2, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify RP in LHR") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("Clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_BSR_CRP_with_blackhole_address_p1(request): + """ + Verify BSR and RP updated correctly after configuring as black hole address + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + step("pre-configure BSM packet") + step("Configure cisco-1 as BSR1 1.1.2.7") + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("configuring loopback address of b1 and b2 as BSR") + intf_lo_addr_b1 = topo["routers"]["b1"]["links"]["lo"]["ipv4"] + + raw_config = { + "b1": { + "raw_config": [ + "interface lo", + "no ip address {}".format(intf_lo_addr_b1), + "ip address {}".format(BSR1_ADDR), + "ip pim", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + GROUP_ADDRESS = "225.200.100.100" + step("configuring static routes for both the BSR") + + next_hop_rp = topo["routers"]["f1"]["links"]["i1"]["ipv4"].split("/")[0] + next_hop_lhr = topo["routers"]["i1"]["links"]["l1"]["ipv4"].split("/")[0] + + input_dict = { + "f1": {"static_routes": [{"network": BSR1_ADDR, "next_hop": NEXT_HOP1}]}, + "i1": {"static_routes": [{"network": BSR1_ADDR, "next_hop": next_hop_rp}]}, + "l1": {"static_routes": [{"network": BSR1_ADDR, "next_hop": next_hop_lhr}]}, + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Use scapy to send pre-defined packet from senser to receiver + + group = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["group"] + CRP = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["candidate_rp"] + step("waiting for BSR to timeout before configuring blackhole route") + clear_bsrp_data(tgen, topo) + + step("Configure black-hole address for BSR and candidate RP") + input_dict = { + "f1": { + "static_routes": [{"network": [BSR1_ADDR, CRP], "next_hop": "blackhole"}] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + intf_f1_i1 = topo["routers"]["f1"]["links"]["i1"]["interface"] + step("Verify bsm transit count is not increamented" "show ip pim interface traffic") + state_dict = {"f1": {intf_f1_i1: ["bsmTx"]}} + + state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("Sending BSR after Configure black hole address for BSR and candidate RP") + step("Send BSR packet from b1 to FHR") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet9") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "l1" + step("Find the elected rp from bsrp-info in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_1, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify if b1 chosen as BSR in l1") + result = verify_pim_bsr(tgen, topo, "l1", BSR_IP_1, expected=False) + assert result is not True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + result = verify_state_incremented(state_before, state_after) + assert result is not True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + step("Remove black-hole address for BSR and candidate RP") + input_dict = { + "f1": { + "static_routes": [ + {"network": [BSR1_ADDR, CRP], "next_hop": "blackhole", "delete": True} + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Sending BSR after removing black-hole address for BSR and candidate RP") + step("Send BSR packet from b1 to FHR") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet9") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify if b1 chosen as BSR in f1") + result = verify_pim_bsr(tgen, topo, "f1", BSR_IP_1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "l1" + group = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["group"] + step("Find the elected rp from bsrp-info in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_1, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify RP in LHR l1") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_new_router_fwd_p0(request): + """ + 1. Verify when new router added to the topology, FRR node will send + unicast BSM to new router + 2. Verify if no forwarding bit is set , FRR is not forwarding the + BSM to other PIM nbrs + 3. Verify multicast BSM is sent to new router when unicast BSM is disabled + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "225.1.1.1" + + # Use scapy to send pre-defined packet from senser to receiver + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0] + time.sleep(1) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify bsr state in FHR + step("Verify if b1 chosen as BSR in f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify bsr state in i1 + step("Verify if b1 chosen as BSR in i1") + result = verify_pim_bsr(tgen, topo, "i1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify ip mroute + iif = "l1-i1-eth0" + src_addr = "*" + oil = "l1-r1-eth1" + + step("Verify mroute populated on l1") + result = verify_ip_mroutes(tgen, "l1", src_addr, GROUP_ADDRESS, iif, oil) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Reload i1 and l1 + step("Reloading i1 and l1. Stop both. bring up l1 and then i1") + stop_router(tgen, "i1") + start_router(tgen, "i1") + stop_router(tgen, "l1") + start_router(tgen, "l1") + + # Verify bsr state in i1 + step("Verify BSR in i1 after restart while no new bsm sent from b1") + result = verify_pim_bsr(tgen, topo, "i1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify bsr state in l1 + step("Verify no BSR in l1 as i1 would not forward the no-forward bsm") + result = verify_pim_bsr(tgen, topo, "l1", bsr_ip, expected=False) + assert result is not True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # unconfigure unicast bsm on f1-i1-eth2 + step("unconfigure unicast bsm on f1-i1-eth2, will forward with only mcast") + enable_disable_pim_unicast_bsm(tgen, "f1", "f1-i1-eth2", enable=False) + + # Reboot i1 to check if still bsm received with multicast address + step("Reboot i1 to check if still bsm received with multicast address") + stop_router(tgen, "i1") + start_router(tgen, "i1") + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify again if BSR is installed from bsm forwarded by f1 + step("Verify again if BSR is installed from bsm forwarded by f1") + result = verify_pim_bsr(tgen, topo, "i1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Use scapy to send pre-defined packet from senser to receiver + step("Send another BSM packet from b1 which will reach l1(LHR)") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet2") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + do_countdown(5) + + # Verify ip mroute populated again + step("Verify mroute again on l1 (lhr)") + result = verify_ip_mroutes(tgen, "l1", src_addr, GROUP_ADDRESS, iif, oil) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_int_bsm_config_p1(request): + """ + 1. Verfiy BSM arrived on non bsm capable interface is dropped and + not processed + 2. Verify group to RP info updated correctly in FRR node, after shut and + no-shut of BSM enable interfaces + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "225.1.1.1" + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0] + time.sleep(1) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Use scapy to send pre-defined packet from senser to receiver + step("Send BSM packet from b1") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify bsr state in i1 + step("Verify if b1 is chosen as BSR in i1") + result = verify_pim_bsr(tgen, topo, "i1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # check if mroute installed + step("check if mroute installed in i1") + iif = "lo" + src_addr = "*" + oil = "i1-l1-eth1" + + result = verify_ip_mroutes(tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # wait till bsm rp age out + step("wait till bsm rp age out") + clear_bsrp_data(tgen, topo) + + # check if mroute uninstalled because of rp age out + step("check if mroute uninstalled because of rp age out in i1") + result = verify_ip_mroutes( + tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil, expected=False + ) + assert result is not True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # unconfigure bsm processing on f1 on f1-i1-eth2 + step("unconfigure bsm processing on f1 in f1-i1-eth2, will drop bsm") + result = enable_disable_pim_bsm(tgen, "f1", "f1-i1-eth2", enable=False) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Use scapy to send pre-defined packet from senser to receiver + step("Send BSM packet from b1") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify bsr state in FHR + step("Verify if b1 chosen as BSR in f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify bsr state in i1 + step("Verify if b1 is not chosen as BSR in i1") + result = verify_pim_bsr(tgen, topo, "i1", bsr_ip, expected=False) + assert result is not True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # check if mroute still not installed because of rp not available + step("check if mroute still not installed because of rp not available") + result = verify_ip_mroutes( + tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil, expected=False + ) + assert result is not True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # configure bsm processing on i1 on f1-i1-eth2 + step("configure bsm processing on f1 in f1-i1-eth2, will accept bsm") + result = enable_disable_pim_bsm(tgen, "f1", "f1-i1-eth2", enable=True) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Use scapy to send pre-defined packet from senser to receiver + step("Send BSM packet again from b1") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet2") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify again if BSR is installed from bsm forwarded by f1 + step("Verify again if BSR is installed from bsm forwarded by f1") + result = verify_pim_bsr(tgen, topo, "i1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # verify ip mroute populated + step("Verify ip mroute") + result = verify_ip_mroutes(tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Shut/No shut the bsm rpf interface and check mroute on lhr(l1) + step("Shut/No shut the bsm rpf interface and check mroute on lhr(l1)") + intf = "l1-i1-eth0" + shutdown_bringup_interface(tgen, "l1", intf, False) + shutdown_bringup_interface(tgen, "l1", intf, True) + + iif = "l1-i1-eth0" + oil = "l1-r1-eth1" + + result = verify_ip_mroutes(tgen, "l1", src_addr, GROUP_ADDRESS, iif, oil) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_static_rp_override_p1(request): + """ + Verify static RP is preferred over BSR + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "225.1.1.1" + # Use scapy to send pre-defined packet from senser to receiver + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0] + time.sleep(1) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify bsr state in FHR + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Check igmp groups + step("Verify IGMP groups in LHR") + dut = "l1" + intf = "l1-r1-eth1" + result = verify_igmp_groups(tgen, dut, intf, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + group = "225.1.1.1/32" + + # Find the elected rp from bsrp-info + step("Find the elected rp from bsrp-info in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Check RP detail in LHR + step("Verify that BS RP in LHR l1") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + iif = "l1-i1-eth0" + # Verify upstream rpf for 225.1.1.1 is chosen as rp1 + step("Verify upstream rpf for 225.1.1.1 is chosen as bsrp") + result = verify_ip_pim_upstream_rpf(tgen, topo, dut, iif, GROUP_ADDRESS, rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Configure a static rp for the group 225.1.1.1/32 + step("Configure a static rp 33.33.33.33 for the group 225.1.1.1/32 in l1") + input_dict = { + "l1": { + "pim": { + "rp": [ + {"rp_addr": "33.33.33.33", "group_addr_range": ["225.1.1.1/32"],} + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verify that static rp is configured over bsrp + static_rp = "33.33.33.33" + step("Verify that Static RP in LHR in l1") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "Static", static_rp) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify if upstream also reflects the static rp + step("Verify upstream rpf for 225.1.1.1 is chosen as static in l1") + result = verify_ip_pim_upstream_rpf(tgen, topo, dut, iif, GROUP_ADDRESS, static_rp) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # delete static rp for the group 225.1.1.1/32 + step("Delete static rp 33.33.33.33 for the group 225.1.1.1/32 in l1") + input_dict = { + "l1": { + "pim": { + "rp": [ + { + "rp_addr": "33.33.33.33", + "group_addr_range": ["225.1.1.1/32"], + "delete": True, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verify if bsrp is installed back for the group 225.1.1.1/32 + step("Verify that BS RP in installed in LHR") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify upstream rpf for 225.1.1.1 is chosen as bsrp + step("Verify upstream rpf for 225.1.1.1 is chosen as bsrp in l1") + result = verify_ip_pim_upstream_rpf(tgen, topo, dut, iif, GROUP_ADDRESS, rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_bsmp_stress_add_del_restart_p2(request): + """ + 1. Verify adding/deleting the group to rp mapping and RP priority + multiple times + 2. Verify RP and (*,G) detail after PIM process restart on FRR node + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "225.1.1.1" + + # Use scapy to send pre-defined packet from senser to receiver + step("Send BSR packet from b1 to FHR") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0] + time.sleep(1) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify bsr state in FHR + step("Verify if b1 is chosen as bsr in f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "l1" + group = "225.1.1.0/24" + # Find the elected rp from bsrp-info + step("Find the elected rp from bsrp-info in LHR l1") + rp1 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp1 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Check RP detail in LHR + step("Verify RP in LHR l1") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp1[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Send BSR packet from b1 after deleting high prio rp for 225.1.1.0/24 + step("Send BSM from b1 to FHR deleting high prio rp for 225.1.1.0/24") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet6") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Find the elected rp from bsrp-info + step("Find the elected rp from bsrp-info in LHR l1") + rp2 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp2 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + logger.info("RP old: %s RP2 new: %s", rp1[group], rp2[group]) + + # Verify is the rp is different now + assert rp1[group] != rp2[group], "Testcase {} :Failed \n Error {}".format( + tc_name, result + ) + + rp_add1 = rp1[group] + rp_add2 = rp2[group] + + # Verify if that rp is installed + step("Verify new RP in LHR installed") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add2) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("Change rp priority in the bsm and send multiple times") + + for i in range(4): + # Send BSR pkt from b1 after putting back high prio rp for 225.1.1.0/24 + step("Send BSM from b1 to FHR put back high prio rp for 225.1.1.0/24") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Find the elected rp from bsrp-info + step("Find the elected rp from bsrp-info in LHR") + rp2 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp2 is not {}, "Testcase {} :Failed \n Error : RP not Found".format( + tc_name + ) + + # Verify is the rp is different now + step("Verify now old RP is elected again") + assert ( + rp_add1 == rp2[group] + ), "Testcase {} :Failed \n Error : rp expected {} rp received {}".format( + tc_name, rp_add1, + ) + + # Verify if that rp is installed + step("Verify old RP in LHR installed") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Send BSR packet from b1 after deleting high prio rp for 225.1.1.0/24 + step("Send BSM from b1 to FHR deleting high prio rp for 225.1.1.0/24") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet6") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify if that rp is installed + step("Verify new RP(rp2) in LHR installed") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add2) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Restart pimd + step("Restarting pimd in LHR") + kill_router_daemons(tgen, "l1", ["pimd"]) + start_router_daemons(tgen, "l1", ["pimd"]) + logger.info("Restarting done") + + # Verify if that rp is installed + step("Verify old RP in LHR installed") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add2) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Send IGMP join to LHR + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + do_countdown(5) + + # VErify mroute created after pimd restart + step("VErify mroute created after pimd restart") + iif = "l1-i1-eth0" + src_addr = "*" + oil = "l1-r1-eth1" + result = verify_ip_mroutes(tgen, "l1", src_addr, GROUP_ADDRESS, iif, oil) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BSM_timeout_p0(request): + """ + Verify BSM timeout on FRR1 + Verify RP state in FRR1 after Bootstrap timer expiry + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "225.1.1.1" + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0] + + # Use scapy to send pre-defined packet from senser to receiver + step("send BSR packet from b1") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Send IGMP join for group 225.1.1.1 from receiver + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify bsr state in FHR + step("Verify bsr state in FHR f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify ip mroute in LHR + step(" Verify ip mroute in LHR l1") + dut = "l1" + iif = "l1-i1-eth0" + src_addr = "*" + oil = "l1-r1-eth1" + result = verify_ip_mroutes(tgen, dut, src_addr, GROUP_ADDRESS, iif, oil) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify join state and join timer + step("Verify join state and join timer in lhr l1") + result = verify_join_state_and_timer(tgen, dut, iif, src_addr, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify upstream IIF interface + step("Verify upstream IIF interface in LHR l1") + result = verify_upstream_iif(tgen, dut, iif, src_addr, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify RP mapping + dut = "l1" + group = "225.1.1.1/32" + step("Verify RP mapping in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp != {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("Waiting for 130 secs to check BSR timeout") + clear_bsrp_data(tgen, topo) + + # Verify if bsr has aged out + step("Verify if bsr has aged out in f1") + no_bsr_ip = "0.0.0.0" + result = verify_pim_bsr(tgen, topo, "f1", no_bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_pim_grp_rp_source( + tgen, topo, "f1", group, rp_source="BSR", expected=False + ) + + assert result is not True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify RP mapping removed after hold timer expires + group = "225.1.1.1/32" + step("Verify RP mapping removed after hold timer expires in l1") + rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp == {}, "Testcase {} :Failed \n Error : RP found when not expected".format( + tc_name + ) + + # Verify iif is unknown after RP timeout + step("Verify iif is unknown after RP timeout in l1") + iif = "Unknown" + result = verify_upstream_iif( + tgen, dut, iif, src_addr, GROUP_ADDRESS, joinState="NotJoined" + ) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify join state and join timer + step("Verify join state and join timer in l1") + iif = "l1-i1-eth0" + result = verify_join_state_and_timer( + tgen, dut, iif, src_addr, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify ip mroute is not installed + step("Verify mroute not installed in l1") + result = verify_ip_mroutes( + tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, expected=False + ) + assert result is not True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_iif_join_state_p0(request): + """ + 1. Verify upstream interfaces(IIF) and join state are updated properly + after BSM received for FRR + 2. Verify IIF and OIL in "show ip pim state" updated properly after + BSM received + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "225.1.1.1" + + # Use scapy to send pre-defined packet from senser to receiver + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0] + time.sleep(1) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify bsr state in FHR + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Check igmp groups + step("Verify IGMP groups in LHR l1") + dut = "l1" + intf = "l1-r1-eth1" + result = verify_igmp_groups(tgen, dut, intf, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + group = "225.1.1.1/32" + + # Find the elected rp from bsrp-info + step("Find the elected rp from bsrp-info in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Check RP detail in LHR + step("Verify RP in LHR l1") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify join state and join timer + step("Verify join state and join timer l1") + iif = "l1-i1-eth0" + src_addr = "*" + result = verify_join_state_and_timer(tgen, dut, iif, src_addr, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify upstream IIF interface + step("Verify upstream IIF interface l1") + result = verify_upstream_iif(tgen, dut, iif, src_addr, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify IIF/OIL in pim state + oil = "l1-r1-eth1" + result = verify_pim_state(tgen, dut, iif, oil, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify ip mroute + src_addr = "*" + step("Verify ip mroute in l1") + result = verify_ip_mroutes(tgen, dut, src_addr, GROUP_ADDRESS, iif, oil) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Make RP unreachanble in LHR + step("Make RP unreachanble in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + next_hop_lhr = topo["routers"]["i1"]["links"]["l1"]["ipv4"].split("/")[0] + + rp_ip = rp[group] + "/32" + input_dict = { + "l1": { + "static_routes": [ + {"network": rp_ip, "next_hop": next_hop_lhr, "delete": True} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Check RP unreachable + step("Check RP unreachability") + iif = "Unknown" + result = verify_upstream_iif( + tgen, dut, iif, src_addr, GROUP_ADDRESS, joinState="NotJoined" + ) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify that it is not installed + step("Verify that it is not installed") + iif = "<iif?>" + result = verify_pim_state(tgen, dut, iif, oil, GROUP_ADDRESS, installed_fl=0) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify mroute not installed + step("Verify mroute not installed") + result = verify_ip_mroutes( + tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, expected=False + ) + assert result is not True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Add back route for RP to make it reachable + step("Add back route for RP to make it reachable") + input_dict = { + "l1": {"static_routes": [{"network": rp_ip, "next_hop": next_hop_lhr,}]} + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify that (*,G) installed in mroute again + iif = "l1-i1-eth0" + result = verify_ip_mroutes(tgen, dut, src_addr, GROUP_ADDRESS, iif, oil) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast-pim-bsm-topo2/mcast_pim_bsmp_02.json b/tests/topotests/multicast-pim-bsm-topo2/mcast_pim_bsmp_02.json new file mode 100644 index 0000000000..14cb0bee1d --- /dev/null +++ b/tests/topotests/multicast-pim-bsm-topo2/mcast_pim_bsmp_02.json @@ -0,0 +1,238 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "routers": { + "b1": { + "links": { + "f1": {"ipv4": "auto", "pim": "enable"}, + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"} + }, + "bsm": { + "bsr_packets": { + "packet1" : { + "data": "01005e00000d005056961165080045c000aa5af500000167372a46000001e000000d2400f5ce165b000001004600000101000018e1010100080800000100090a090a0096650001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e1010101010100000100050606050096000001000020e20101010101000001000909090900960000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"], + "225.1.1.1/32": ["5.6.6.5/32"], + "225.200.100.100/32": ["210.210.210.210/32"], + "226.1.1.1/32": ["9.9.9.9/32"] + + }, + "Desc" : "Packet with 3 group range - rp prio different" + }, + "packet2" : { + "data": "01005e00000d005056961165080045c0009420f400000167714146000001e000000d24000b3b164a000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e20101010101000001000909090900000000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"] + }, + "Desc" : "Packet 1 with hold time 0 for 226.1.1.1/32" + }, + "packet3" : { + "data": "01005e00000d005056961165080045c000944d0000000167453546000001e000000d2400e52b17c3000001004600000101000018e1010100080800000100090a090a0096650001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e20101010101000001000909090900960000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"], + "226.1.1.1/32": ["9.9.9.9/32"] + }, + "Desc" : "BSR Prio - TC 4" + }, + "packet4" : { + "data": "01005e00000d005056961165080045c000aa3d1c00000167550346000001e000000d24000d671c52000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e1010101010100000100090909090000000001000020e20101010101000001000909090900960000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"], + "225.1.1.1/32": ["9.9.9.9/32"], + "226.1.1.1/32": ["9.9.9.9/32"] + }, + "Desc" : "TC - 5" + }, + "packet5" : { + "data": "01005e00000d005056961165080045c000aa3d1c00000167550346000001e000000d24000d671c52000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e1010101010100000100090909090000000001000020e20101010101000001000909090900960000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"], + "226.1.1.1/32": ["9.9.9.9/32"] + }, + "Desc" : "TC - 5, 225.1.1.1 with hold time 0" + }, + "packet6" : { + "data": "01005e00000d005056961165080045c0008a795e0000016718e146000001e000000d24006cc509d5000001004600000101000018e10101000707000001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e20101010101000001000909090900960000", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"], + "226.1.1.1/32": ["9.9.9.9/32"] + }, + "Desc" : "TC - 6,High prio rp removed on 225.1.1.0/24" + }, + "packet7" : { + "data": "01005e00000d005056961165080045c0007e6ebb00000167239046000001e000000d2400090810b3000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a00966400", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32", + "9.9.9.10/32", "7.7.9.7/32", + "7.7.2.7/32", "7.5.2.7/32", + "7.2.2.7/32", "2.2.2.2/32"] + }, + "Desc" : "TC - 8, rps with same priority" + }, + + "packet8" : { + "data": "01005e00000d005056b76687080045c000383cdf0000016755b246000001e000000d24008ad51a9f000001004600000101000020e1c86464010100000100d2d2d2d200960000", + "group": "225.200.100.100/32", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "70.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.200.100.100/32": ["210.210.210.210/32"] + }, + "Desc" : "TC - 30, grp add with all octet" + }, + + "packet9" : { + "data": "01005e00000d005056b76687080045c000387b8600000167170b46000001e000000d2400c6282245000001000101020701000020e1c86464010100000100d2d2d2d200960000", + "group": "225.200.100.100/32", + "candidate_rp": "210.210.210.210/32", + "src_ip": "70.0.0.1/24", + "dest_ip": "70.0.0.2/24", + "bsr": "1.1.2.7/32", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.200.100.100/32": ["210.210.210.210/32"] + }, + "Desc" : "TC -29, BSM with preferred ip" + } + + } + } + }, + + "b2": { + "links": { + "f1": {"ipv4": "auto", "pim": "enable"}, + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"} + }, + "bsm": { + "bsr_packets": { + "packet1" : { + "data": "01005e00000d005056b70489080045c0003865db0000016731b641000001e000000d2400659c0c6f000001004100000101000018e10101000101000001002121212100960000", + "src_ip": "65.0.0.1/24", + "dest_ip": "65.0.0.2/24", + "bsr": "65.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["33.33.33.33/32"], + "225.200.100.100/32": ["210.210.210.210/32"] + } + }, + "packet2" : { + "data": "01005e00000d005056b70489080045c00038663000000167316141000001e000000d24006dce0433000a01004100000101000018e10101000101000001002121212100960000", + "src_ip": "65.0.0.1/24", + "dest_ip": "65.0.0.2/24", + "bsr": "65.0.0.1/24", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.1.1.0/24": ["33.33.33.33/32"] + } + }, + + "packet3" : { + "data": "01005e00000d005056b76687080045c00038f5c800000167a1c841000001e000000d2400c6621a10000001000a02010101000020e1c86464010100000100d2d2d2d200960000", + "src_ip": "65.0.0.1/24", + "dest_ip": "65.0.0.2/24", + "bsr": "10.2.1.1/32", + "pkt_dst": "224.0.0.13", + "rp_mapping" : { + "225.200.100.100/32": ["210.210.210.210/32"] + } + } + + } + } + }, + + "f1": { + "links": { + "b1": {"ipv4": "auto", "pim": "enable"}, + "b2": {"ipv4": "auto", "pim": "enable"}, + "i1": {"ipv4": "auto", "pim": "enable"}, + "s1": {"ipv4": "auto", "pim": "enable"} + } + }, + "i1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"} + } + }, + "l1": { + "links": { + "i1": {"ipv4": "auto", "pim": "enable"}, + "r1": {"ipv4": "auto", "pim": "enable"} + }, + "igmp": { + "interfaces": { + "l1-r1-eth1" :{ + "igmp":{ + "version": "2" + } + } + } + } + }, + "s1": { + "links": { + "f1": {"ipv4": "auto", "pim": "enable"} + } + }, + "r1": { + "links": { + "l1": {"ipv4": "auto", "pim": "disable"} + } + } + } +} diff --git a/tests/topotests/multicast-pim-bsm-topo2/test_mcast_pim_bsmp_02.py b/tests/topotests/multicast-pim-bsm-topo2/test_mcast_pim_bsmp_02.py new file mode 100644 index 0000000000..459afb5a02 --- /dev/null +++ b/tests/topotests/multicast-pim-bsm-topo2/test_mcast_pim_bsmp_02.py @@ -0,0 +1,1115 @@ +#!/usr/bin/env python +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +""" +Following tests are covered to test PIM BSM processing basic functionality: + +Test steps +- Create topology (setup module) +- Bring up topology + +Tests covered in this suite +1. Verify (*,G) mroute detail on FRR router after BSM rp installed +2. Verify group to RP updated correctly on FRR router, when BSR advertising + the overlapping group address +3. Verify group to RP info is updated correctly, when BSR advertising the + same RP with different priority +4. Verify group to RP mapping in FRR node when 2 BSR are present in the network + and both are having same BSR priority +5. Verify RP is selected based on hash function, when BSR advertising the group + to RP mapping with same priority +6. Verify fragmentation of bootstrap message +7. Verify when candidate RP advertised with 32 mask length + and contain all the contacts +""" + +import os +import sys +import json +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + addKernelRoute, + create_static_routes, + iperfSendIGMPJoin, + stop_router, + start_router, + shutdown_bringup_interface, + kill_router_daemons, + start_router_daemons, + reset_config_on_routers, + do_countdown, + apply_raw_config, + kill_iperf, + run_frr_cmd, + required_linux_kernel_version, + topo_daemons, +) + +from lib.pim import ( + create_pim_config, + add_rp_interfaces_and_pim_config, + reconfig_interfaces, + scapy_send_bsr_raw_packet, + find_rp_from_bsrp_info, + verify_pim_grp_rp_source, + verify_pim_bsr, + verify_ip_mroutes, + verify_join_state_and_timer, + verify_pim_state, + verify_upstream_iif, + verify_igmp_groups, + verify_ip_pim_upstream_rpf, + enable_disable_pim_unicast_bsm, + enable_disable_pim_bsm, + clear_ip_mroute, + clear_ip_pim_interface_traffic, + verify_pim_interface_traffic, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/mcast_pim_bsmp_02.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +TOPOLOGY = """ + + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + +""" +# Global variables +NEXT_HOP1 = "70.0.0.1" +NEXT_HOP2 = "65.0.0.1" +BSR_IP_1 = "1.1.2.7" +BSR_IP_2 = "10.2.1.1" +BSR1_ADDR = "1.1.2.7/32" +BSR2_ADDR = "10.2.1.1/32" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Local APIs +# +##################################################### + + +def clear_bsrp_data(tgen, topo): + + """ + clear bsm databas after test" + Parameters + ---------- + * `tgen`: topogen object + + Usage + ----- + result = clear_bsrp_data(tgen, topo) + Returns + ------- + errormsg(str) or True + """ + + for dut in tgen.routers(): + + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: clear_bsrp_data") + + run_frr_cmd(rnode, "clear ip pim bsr-data") + + return True + + +def pre_config_to_bsm(tgen, topo, tc_name, bsr, sender, receiver, fhr, rp, lhr, packet): + """ + API to do required configuration to send and receive BSR packet + """ + + # Re-configure interfaces as per BSR packet + result = reconfig_interfaces(tgen, topo, bsr, fhr, packet) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Create static routes + if "bsr" in topo["routers"][bsr]["bsm"]["bsr_packets"][packet]: + bsr_route = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["bsr"] + next_hop = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["src_ip"].split( + "/" + )[0] + next_hop_rp = topo["routers"][fhr]["links"][rp]["ipv4"].split("/")[0] + next_hop_lhr = topo["routers"][rp]["links"][lhr]["ipv4"].split("/")[0] + + # Add static routes + input_dict = { + fhr: {"static_routes": [{"network": bsr_route, "next_hop": next_hop}]}, + rp: {"static_routes": [{"network": bsr_route, "next_hop": next_hop_rp}]}, + lhr: {"static_routes": [{"network": bsr_route, "next_hop": next_hop_lhr}]}, + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Add kernal route for source + group = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["pkt_dst"] + bsr_interface = topo["routers"][bsr]["links"][fhr]["interface"] + result = addKernelRoute(tgen, bsr, bsr_interface, group) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # RP Mapping + rp_mapping = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["rp_mapping"] + + # Add interfaces in RP for all the RPs + result = add_rp_interfaces_and_pim_config(tgen, topo, "lo", rp, rp_mapping) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Add kernal routes to sender and receiver + for group, rp_list in rp_mapping.items(): + mask = group.split("/")[1] + if int(mask) == 32: + group = group.split("/")[0] + + # Add kernal routes for sender + s_interface = topo["routers"][sender]["links"][fhr]["interface"] + result = addKernelRoute(tgen, sender, s_interface, group) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Add kernal routes for receiver + r_interface = topo["routers"][receiver]["links"][lhr]["interface"] + result = addKernelRoute(tgen, receiver, r_interface, group) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Add static routes for RPs in FHR and LHR + next_hop_fhr = topo["routers"][rp]["links"][fhr]["ipv4"].split("/")[0] + next_hop_lhr = topo["routers"][rp]["links"][lhr]["ipv4"].split("/")[0] + input_dict = { + fhr: {"static_routes": [{"network": rp_list, "next_hop": next_hop_fhr}]}, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + input_dict = { + lhr: {"static_routes": [{"network": rp_list, "next_hop": next_hop_lhr}]}, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + return True + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_starg_mroute_p0(request): + """ + 1. Verify (*,G) mroute detail on FRR router after BSM rp installed + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "226.1.1.1" + + # Use scapy to send pre-defined packet from senser to receiver + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0] + time.sleep(1) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify bsr state in FHR + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Check igmp groups + step("Verify IGMP groups in LHR l1") + dut = "l1" + intf = "l1-r1-eth1" + result = verify_igmp_groups(tgen, dut, intf, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + group = "226.1.1.1/32" + src_addr = "*" + + # Find the elected rp from bsrp-info + step("Find the elected rp from bsrp-info in LHR in l1") + rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Check RP detail in LHR + step("Verify RP in LHR in l1") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify join state and join timer + step("Verify join state and join timer in l1") + iif = "l1-i1-eth0" + result = verify_join_state_and_timer(tgen, dut, iif, src_addr, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify upstream IIF interface + step("Verify upstream IIF interface in l1") + result = verify_upstream_iif(tgen, dut, iif, src_addr, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify IIF/OIL in pim state + oil = "l1-r1-eth1" + result = verify_pim_state(tgen, dut, iif, oil, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify ip mroute + step("Verify ip mroute in l1") + src_addr = "*" + result = verify_ip_mroutes(tgen, dut, src_addr, GROUP_ADDRESS, iif, oil) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Remove the group rp mapping and send bsm + step("Remove the grp-rp mapping by sending bsm with hold time 0 for grp-rp") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet2") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Check RP unreachable + step("Check RP unreachability in l1") + iif = "Unknown" + result = verify_upstream_iif( + tgen, dut, iif, src_addr, GROUP_ADDRESS, joinState="NotJoined" + ) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify that it is not installed + step("Verify that iif is not installed in l1") + iif = "<iif?>" + result = verify_pim_state(tgen, dut, iif, oil, GROUP_ADDRESS, installed_fl=0) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify mroute not installed + step("Verify mroute not installed in l1") + result = verify_ip_mroutes( + tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, wait=20, expected=False + ) + assert result is not True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Send BSM again to configure rp + step("Add back RP by sending BSM from b1") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify that (*,G) installed in mroute again + iif = "l1-i1-eth0" + result = verify_ip_mroutes(tgen, dut, src_addr, GROUP_ADDRESS, iif, oil) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_overlapping_group_p0(request): + """ + Verify group to RP updated correctly on FRR router, when BSR advertising + the overlapping group address + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "225.1.1.1" + + # Use scapy to send pre-defined packet from senser to receiver + step("Send BSR packet from b1 to FHR") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet4") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0] + time.sleep(1) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify bsr state in FHR + step("Verify if b1 is chosen as bsr in f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "l1" + group1 = "225.1.1.1/32" + # Find the elected rp from bsrp-info fro group 225.1.1.1/32 + step("Find the elected rp from bsrp-info in LHR for 225.1.1.1/32") + rp1 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group1) + assert rp1 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + group2 = "225.1.1.0/24" + # Find the elected rp from bsrp-info fro group 225.1.1.0/24 + step("Find the elected rp from bsrp-info in LHR for 225.1.1.0/24") + rp2 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group2) + assert rp2 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + iif = "l1-i1-eth0" + # Verify upstream rpf for 225.1.1.1 is chosen as rp1 + step("Verify upstream rpf for 225.1.1.1 is chosen as rp1 in l1") + result = verify_ip_pim_upstream_rpf(tgen, topo, dut, iif, GROUP_ADDRESS, rp1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Send BSR packet from b1 with rp for 225.1.1.1/32 removed + step("Send BSR packet from b1 with rp for 225.1.1.1/32 removed") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet5") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify upstream rpf for 225.1.1.1 is chosen as rp1 + step("Verify upstream rpf for 225.1.1.1 is chosen as rp2 in l1") + result = verify_ip_pim_upstream_rpf(tgen, topo, dut, iif, GROUP_ADDRESS, rp2) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify IIF/OIL in pim state + step("Verify iif is installed after rp change in l1") + oil = "l1-r1-eth1" + result = verify_pim_state(tgen, dut, iif, oil, GROUP_ADDRESS) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_RP_priority_p0(request): + """ + Verify group to RP info is updated correctly, when BSR advertising the + same RP with different priority + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "225.1.1.1" + + # Use scapy to send pre-defined packet from senser to receiver + step("Send BSR packet from b1 to FHR") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0] + time.sleep(1) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify bsr state in FHR + step("Verify if b1 is chosen as bsr in f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "l1" + group = "225.1.1.0/24" + # Find the elected rp from bsrp-info + step("Find the elected rp from bsrp-info in LHR l1") + rp1 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp1 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Check RP detail in LHR + step("Verify RP in LHR l1") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp1[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Send BSR packet from b1 after deleting high prio rp for 225.1.1.0/24 + step("Send BSM from b1 to FHR deleting high prio rp for 225.1.1.0/24") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet6") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Find the elected rp from bsrp-info + step("Find the elected rp from bsrp-info in LHR l1") + rp2 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp2 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + logger.info("RP old: {} RP2 new: {} ".format(rp1[group], rp2[group])) + + # Verify is the rp is different now + assert rp1[group] != rp2[group], "Testcase {} :Failed \n Error {}".format( + tc_name, result + ) + + rp_add1 = rp1[group] + rp_add2 = rp2[group] + + # Verify if that rp is installed + step("Verify new RP in LHR installed") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add2) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Send BSR packet from b1 after putting back high prio rp for 225.1.1.0/24 + step("Send BSM from b1 to FHR put back old high prio rp for 225.1.1.0/24") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Find the elected rp from bsrp-info + step("Find the elected rp from bsrp-info in LHR") + rp2 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp2 is not {}, "Testcase {} :Failed \n Error : RP not Found".format(tc_name) + + # Verify is the rp is different now + step("Verify now old RP is elected again") + assert ( + rp_add1 == rp2[group] + ), "Testcase {} :Failed \n Error : rp expected {} rp received {}".format( + tc_name, rp_add1, + ) + + # Verify if that rp is installed + step("Verify new RP in LHR installed") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_BSR_election_p0(request): + """ + Verify group to RP mapping in FRR node when 2 BSR are present in the network + and both are having same BSR priority + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "225.1.1.1" + + # Use scapy to send pre-defined packet from senser to receiver + step("Send BSR packet from b1 to FHR") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet3") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + bsr_ip1 = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[ + 0 + ] + time.sleep(1) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify bsr state in FHR + step("Verify if b1 is chosen as bsr in f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "l1" + group = "225.1.1.0/24" + # Find the elected rp from bsrp-info + step("Find the elected rp from bsrp-info in LHR in l1") + rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip1, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Check RP detail in LHR + step("Verify RP in LHR l1") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Send BSR packet from b2 with same priority + step("Send BSR packet from b2 to FHR with same priority") + result = scapy_send_bsr_raw_packet(tgen, topo, "b2", "f1", "packet1") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + bsr_ip2 = topo["routers"]["b2"]["bsm"]["bsr_packets"]["packet2"]["bsr"].split("/")[ + 0 + ] + time.sleep(1) + + logger.info("BSR b1:" + bsr_ip1 + " BSR b2:" + bsr_ip2) + # Verify bsr state in FHR + step("Verify if b2 is not chosen as bsr in f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip2, expected=False) + assert result is not True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify if b1 is still chosen as bsr + step("Verify if b1 is still chosen as bsr in f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip1) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify if that rp is installed + step("Verify that same RP in istalled in LHR l1") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_RP_hash_p0(request): + """ + Verify RP is selected based on hash function, when BSR advertising the group + to RP mapping with same priority + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "225.1.1.1" + + # Use scapy to send pre-defined packet from senser to receiver + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet7") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0] + time.sleep(1) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + dut = "l1" + + # Verify bsr state in FHR + step("Verify if b1 chosen as BSR in f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + group = "225.1.1.0/24" + + # Find the elected rp from bsrp-info + step("Find the elected rp from bsrp-info in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify if RP with highest hash value is chosen + step("Verify if RP(2.2.2.2) with highest hash value is chosen in l1") + if rp[group] == "2.2.2.2": + result = True + else: + result = "rp expected: 2.2.2.2 got:" + rp[group] + + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Check RP detail in LHR + step("Verify RP in LHR") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_BSM_fragmentation_p1(request): + """ + Verify fragmentation of bootstrap message + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + reset_config_on_routers(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = pre_config_to_bsm( + tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + GROUP_ADDRESS = "225.1.1.1" + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0] + + step("Send BSM and verify if all routers have same bsrp before fragment") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1") + # Verify bsr state in FHR + step("Verify if b1 chosen as BSR in f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + fhr_node = tgen.routers()["f1"] + inter_node = tgen.routers()["i1"] + lhr_node = tgen.routers()["l1"] + + # Verify if bsrp list is same across f1, i1 and l1 + step("Verify if bsrp list is same across f1, i1 and l1") + bsrp_f1 = fhr_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True) + logger.info("show_ip_pim_bsrp_info_json f1: \n %s", bsrp_f1) + bsrp_i1 = inter_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True) + logger.info("show_ip_pim_bsrp_info_json i1: \n %s", bsrp_i1) + bsrp_l1 = lhr_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True) + logger.info("show_ip_pim_bsrp_info_json l1: \n %s", bsrp_l1) + + if bsrp_f1 == bsrp_l1: + result = True + else: + result = "bsrp info in f1 is not same in l1" + + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # set mtu of fhr(f1) to i1 interface to 100 so that bsm fragments + step("set mtu of fhr(f1) to i1 interface to 100 so that bsm fragments") + fhr_node.run("ifconfig f1-i1-eth2 mtu 100") + inter_node.run("ifconfig i1-f1-eth0 mtu 100") + + # Use scapy to send pre-defined packet from senser to receiver + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet2") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + # Verify bsr state in FHR + step("Verify if b1 chosen as BSR") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verify if bsrp list is same across f1, i1 and l1 + step("Verify if bsrp list is same across f1, i1 and l1 after fragmentation") + bsrp_f1 = fhr_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True) + logger.info("show_ip_pim_bsrp_info_json f1: \n %s", bsrp_f1) + bsrp_i1 = inter_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True) + logger.info("show_ip_pim_bsrp_info_json i1: \n %s", bsrp_i1) + bsrp_l1 = lhr_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True) + logger.info("show_ip_pim_bsrp_info_json l1: \n %s", bsrp_l1) + + if bsrp_f1 == bsrp_l1: + result = True + else: + result = "bsrp info in f1 is not same in l1" + + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +def test_RP_with_all_ip_octet_p1(request): + """ + Verify when candidate RP advertised with 32 mask length + and contain all the contacts + + Topology used: + b1_____ + | + | + s1-----f1-----i1-----l1----r1 + | + ______| + b2 + + b1 - BSR 1 + b2 - BSR 2 + s1 - Source + f1 - FHR + i1 - Intermediate Router (also RP) + r1 - Receiver + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step("pre-configure BSM packet") + result = pre_config_to_bsm( + tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Send the IGMP group (225.100.100.100) from receiver connected to FRR") + GROUP_ADDRESS = "225.200.100.100" + + # Use scapy to send pre-defined packet from senser to receiver + step("Configure cisco-1 as BSR1") + result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet8") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet8"]["bsr"].split("/")[0] + time.sleep(1) + + result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + dut = "l1" + step( + "Groups are shown with candidate RP with correct mask length 'show ip pim bsrp-info'" + ) + step("Verify if b1 chosen as BSR in f1") + result = verify_pim_bsr(tgen, topo, "f1", bsr_ip) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + group = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["group"] + step("Find the elected rp from bsrp-info in LHR l1") + rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group) + assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify RP in LHR") + result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group]) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("clear BSM database before moving to next case") + clear_bsrp_data(tgen, topo) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast-pim-sm-topo1/multicast_pim_sm_topo1.json b/tests/topotests/multicast-pim-sm-topo1/multicast_pim_sm_topo1.json new file mode 100644 index 0000000000..71454c2ab2 --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo1/multicast_pim_sm_topo1.json @@ -0,0 +1,140 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "routers": { + "l1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "i1": {"ipv4": "auto", "pim": "enable"}, + "i6": {"ipv4": "auto", "pim": "enable"}, + "i7": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"} + }, + "igmp": { + "interfaces": { + "l1-i1-eth1" :{ + "igmp":{ + "version": "2" + } + } + } + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.7.0/24", "10.0.6.0/24", "10.0.9.0/24"], + "next_hop": "10.0.12.2" + }, + { + "network": ["1.0.1.2/32", "1.0.3.5/32", "10.0.1.0/24", "1.0.2.2/32", "10.0.4.0/24", "10.0.3.0/24"], + "next_hop": "10.0.2.1" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i3": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.0.5.0/24", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24"], + "next_hop": "10.0.7.1" + }, + { + "network": ["1.0.1.2/32", "10.0.8.0/24", "10.0.10.0/24", "10.0.4.0/24", "10.0.0.0/24", "10.0.11.0/24", "10.0.1.0/24", "10.0.2.0/24"], + "next_hop": "10.0.12.1" + }] + }, + "f1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "i2": {"ipv4": "auto", "pim": "enable"}, + "i8": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.12.0/24", "10.0.11.0/24"], + "next_hop": "10.0.7.2" + }, + { + "network": ["1.0.2.2/32", "10.0.1.0/24", "10.0.0.0/24", "10.0.4.0/24", "1.0.1.2/32"], + "next_hop": "10.0.3.1" + }] + }, + "c1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "i4": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.6.0/24", "10.0.3.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.12.0/24", "10.0.10.0/24", "10.0.11.0/24"], + "next_hop": "10.0.2.2" + }, + { + "network": ["10.0.5.0/24", "10.0.7.0/24", "1.0.3.5/32", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24", "10.0.4.0/24"], + "next_hop": "10.0.0.2" + }] + }, + "c2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i5": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.7.0/24", "10.0.10.0/24", "10.0.11.0/24"], + "next_hop": "10.0.3.2" + }, + { + "network": ["1.0.1.2/32", "10.0.4.0/24", "10.0.2.0/24"], + "next_hop": "10.0.0.1" + }] + }, + "i1": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i2": { + "links": { + "f1": {"ipv4": "auto"} + } + }, + "i3": { + "links": { + "r2": {"ipv4": "auto"} + } + }, + "i4": { + "links": { + "c1": {"ipv4": "auto"} + } + }, + "i5": { + "links": { + "c2": {"ipv4": "auto"} + } + }, + "i6": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i7": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i8": { + "links": { + "f1": {"ipv4": "auto"} + } + } + } +} diff --git a/tests/topotests/multicast-pim-sm-topo1/test_multicast_pim_sm_topo1.py b/tests/topotests/multicast-pim-sm-topo1/test_multicast_pim_sm_topo1.py new file mode 100755 index 0000000000..ac675c5c2f --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo1/test_multicast_pim_sm_topo1.py @@ -0,0 +1,1698 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +""" +Following tests are covered to test multicast pim sm: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: +1. TC_1_1: Verify Multicast data traffic with static RP, (*,g) and + (s,g) OIL updated correctly +2. TC_1_2: Verify Multicast data traffic with static RP, (*,g) and + (s,g) OIL updated correctly +3. TC_4: Verify removing the RP should not impact the multicast + data traffic +4. TC_5: Verify (*,G) and (S,G) entry populated again after clear the + PIM nbr and mroute from FRR node +5. TC_9: Verify (s,g) timeout from FHR and RP when same receive + exist in LHR , FHR and RP +6. TC_19: Verify mroute detail when same receiver joining 5 + different sources +7. TC_16: Verify (*,G) and (S,G) populated correctly + when FRR is the transit router +8. TC_23: Verify (S,G) should not create if RP is not reachable +9. TC_24: Verify modification of IGMP query timer should get update + accordingly +10. TC_25: Verify modification of IGMP max query response timer + should get update accordingly +""" + +import os +import sys +import json +import time +import datetime +from time import sleep +import pytest + +pytestmark = pytest.mark.pimd + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + iperfSendIGMPJoin, + addKernelRoute, + reset_config_on_routers, + iperfSendTraffic, + kill_iperf, + shutdown_bringup_interface, + kill_router_daemons, + start_router, + start_router_daemons, + stop_router, + required_linux_kernel_version, + topo_daemons, +) +from lib.pim import ( + create_pim_config, + create_igmp_config, + verify_igmp_groups, + verify_ip_mroutes, + verify_pim_interface_traffic, + verify_upstream_iif, + verify_pim_neighbors, + verify_pim_state, + verify_ip_pim_join, + clear_ip_mroute, + clear_ip_pim_interface_traffic, + verify_igmp_config, + clear_ip_mroute_verify +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/multicast_pim_sm_topo1.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +TOPOLOGY = """ + + + i4-----c1-------------c2---i5 + | | + | | + i1-----l1------r2-----f1---i2 + | | | | + | | | | + i7 i6 i3 i8 + + Description: + i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP + join and traffic + l1 - LHR + f1 - FHR + r2 - FRR router + c1 - FRR router + c2 - FRR router +""" + +# Global variables +GROUP_RANGE = "225.0.0.0/8" +IGMP_JOIN = "225.1.1.1" +GROUP_RANGE_1 = [ + "225.1.1.1/32", + "225.1.1.2/32", + "225.1.1.3/32", + "225.1.1.4/32", + "225.1.1.5/32", +] +IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"] +GROUP_RANGE_2 = [ + "226.1.1.1/32", + "226.1.1.2/32", + "226.1.1.3/32", + "226.1.1.4/32", + "226.1.1.5/32", +] +IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"] + +GROUP_RANGE_3 = [ + "227.1.1.1/32", + "227.1.1.2/32", + "227.1.1.3/32", + "227.1.1.4/32", + "227.1.1.5/32", +] +IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"] + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.19") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, iperf, iperf_intf, GROUP_RANGE, join=False, traffic=False +): + """ + API to do pre-configuration to send IGMP join and multicast + traffic + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `iperf`: router running iperf + * `iperf_intf`: interface name router running iperf + * `GROUP_RANGE`: group range + * `join`: IGMP join, default False + * `traffic`: multicast traffic, default False + """ + + if join: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + if traffic: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + router_list = tgen.routers() + for router in router_list.keys(): + if router == iperf: + continue + + rnode = router_list[router] + rnode.run("echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter") + + return True + + +def verify_state_incremented(state_before, state_after): + """ + API to compare interface traffic state incrementing + + Parameters + ---------- + * `state_before` : State dictionary for any particular instance + * `state_after` : State dictionary for any particular instance + """ + + for router, state_data in state_before.items(): + for state, value in state_data.items(): + if state_before[router][state] >= state_after[router][state]: + errormsg = ( + "[DUT: %s]: state %s value has not" + " incremented, Initial value: %s, " + "Current value: %s [FAILED!!]" + % ( + router, + state, + state_before[router][state], + state_after[router][state], + ) + ) + return errormsg + + logger.info( + "[DUT: %s]: State %s value is " + "incremented, Initial value: %s, Current value: %s" + " [PASSED!!]", + router, + state, + state_before[router][state], + state_after[router][state], + ) + + return True + + +def test_multicast_data_traffic_static_RP_send_join_then_traffic_p0(request): + """ + TC_1_1: Verify Multicast data traffic with static RP, (*,g) and + (s,g) OIL updated correctly + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)") + intf_i1_l1 = topo["routers"]["i1"]["links"]["l1"]["interface"] + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i1", intf_i1_l1, GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("joinRx value before join sent") + intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"] + state_dict = {"r2": {intf_r2_l1: ["joinRx"]}} + state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase {} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send the IGMP join first and then start the traffic") + + step("Configure RP on R2 (loopback interface) for the" " group range 225.0.0.0/8") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to 225.1.1.1 receiver") + intf_i2_f1 = topo["routers"]["i2"]["links"]["f1"]["interface"] + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i2", intf_i2_f1, GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ip mroute' showing correct RPF and OIF" + " interface for (*,G) and (S,G) entries on all the nodes" + ) + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"] + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"] + intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"] + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + input_dict = [ + {"dut": "l1", "src_address": "*", "iif": intf_l1_r2, "oil": intf_l1_i1}, + {"dut": "l1", "src_address": source, "iif": intf_l1_r2, "oil": intf_l1_i1}, + {"dut": "r2", "src_address": "*", "iif": "lo", "oil": intf_r2_l1}, + {"dut": "r2", "src_address": source, "iif": intf_r2_f1, "oil": intf_r2_l1}, + {"dut": "f1", "src_address": source, "iif": intf_f1_i2, "oil": intf_f1_r2}, + ] + + for data in input_dict: + result = verify_ip_mroutes( + tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"] + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes" + ) + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("joinRx value after join sent") + state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_after, dict + ), "Testcase {} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step( + "l1 sent PIM (*,G) join to r2 verify using" + "'show ip pim interface traffic' on RP connected interface" + ) + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("l1 sent PIM (S,G) join to f1 , verify using 'show ip pim join'") + dut = "f1" + interface = intf_f1_r2 + result = verify_ip_pim_join(tgen, topo, dut, interface, IGMP_JOIN) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_multicast_data_traffic_static_RP_send_traffic_then_join_p0(request): + """ + TC_1_2: Verify Multicast data traffic with static RP, (*,g) and + (s,g) OIL updated correctly + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure RP on R2 (loopback interface) for the" " group range 225.0.0.0/8") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Start traffic first and then send the IGMP join") + + step("Send multicast traffic from FRR3 to 225.1.1.1 receiver") + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)") + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("joinRx value before join sent") + state_dict = {"r2": {"r2-l1-eth2": ["joinRx"]}} + state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase {} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ip mroute' showing correct RPF and OIF" + " interface for (*,G) and (S,G) entries on all the nodes" + ) + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "l1", "src_address": "*", "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + {"dut": "r2", "src_address": "*", "iif": "lo", "oil": "r2-l1-eth2"}, + {"dut": "r2", "src_address": source, "iif": "r2-f1-eth0", "oil": "r2-l1-eth2"}, + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"}, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"] + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes" + ) + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("joinRx value after join sent") + state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_after, dict + ), "Testcase {} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step( + "l1 sent PIM (*,G) join to r2 verify using" + "'show ip pim interface traffic' on RP connected interface" + ) + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("l1 sent PIM (S,G) join to f1 , verify using 'show ip pim join'") + dut = "f1" + interface = "f1-r2-eth3" + result = verify_ip_pim_join(tgen, topo, dut, interface, IGMP_JOIN) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_clear_pim_neighbors_and_mroute_p0(request): + """ + TC_5: Verify (*,G) and (S,G) entry populated again after clear the + PIM nbr and mroute from FRR node + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP on c1 for group (225.1.1.1-5)") + input_dict = { + "c1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Enable IGMP on FRR1 interface and send IGMP join 225.1.1.1 " + "to 225.1.1.5 from different interfaces" + ) + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3, wait for SPT switchover") + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, "i2", IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Clear the mroute on l1, wait for 5 sec") + result = clear_ip_mroute_verify(tgen, "l1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "After clear ip mroute (*,g) entries are re-populated again" + " with same OIL and IIF, verify using 'show ip mroute' and " + " 'show ip pim upstream' " + ) + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"} + ] + + for data in input_dict: + result = verify_ip_mroutes( + tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"] + ) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes" + ) + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN + ) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_mroute_when_same_receiver_in_FHR_LHR_and_RP_p0(request): + """ + TC_9: Verify (s,g) timeout from FHR and RP when same receive + exist in LHR , FHR and RP + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure RP on R2 (loopback interface) for the" " group range 225.0.0.0/8") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1) to R1") + + input_dict = { + "f1": {"igmp": {"interfaces": {"f1-i8-eth2": {"igmp": {"version": "2"}}}}}, + "r2": {"igmp": {"interfaces": {"r2-i3-eth1": {"igmp": {"version": "2"}}}}}, + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i1": "i1-l1-eth0", "i8": "i8-f1-eth0", "i3": "i3-r2-eth0"} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from R3 to 225.1.1.1 receiver") + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("IGMP is received on FRR1 , FRR2 , FRR3, using " "'show ip igmp groups'") + igmp_groups = {"l1": "l1-i1-eth1", "r2": "r2-i3-eth1", "f1": "f1-i8-eth2"} + for dut, interface in igmp_groups.items(): + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("(*,G) present on all the node with correct OIL" " using 'show ip mroute'") + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "l1", "src_address": "*", "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + {"dut": "r2", "src_address": "*", "iif": "lo", "oil": "r2-i3-eth1"}, + {"dut": "r2", "src_address": source, "iif": "r2-f1-eth0", "oil": "r2-i3-eth1"}, + {"dut": "f1", "src_address": "*", "iif": "f1-r2-eth3", "oil": "f1-i8-eth2"}, + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-i8-eth2"}, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"] + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_mroute_when_same_receiver_joining_5_diff_sources_p0(request): + """ + TC_19: Verify mroute detail when same receiver joining 5 + different sources + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP for (226.1.1.1-5) and (232.1.1.1-5)" " in c1") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_dict = { + "c1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": _GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Configure IGMP interface on FRR1 and FRR3 and send IGMP join" + "for group (226.1.1.1-5, 232.1.1.1-5)" + ) + + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i1", "i1-l1-eth0", _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i1", _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict = { + "f1": {"igmp": {"interfaces": {"f1-i8-eth2": {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i8", "i8-f1-eth0", _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i8", _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Send multicast traffic from all the sources to all the " + "receivers (226.1.1.1-5, 232.1.1.1-5)" + ) + + input_traffic = { + "i6": "i6-l1-eth0", + "i7": "i7-l1-eth0", + "i3": "i3-r2-eth0", + "i4": "i4-c1-eth0", + "i5": "i5-c2-eth0", + } + + for src, src_intf in input_traffic.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify (*,G) are created on FRR1 and FRR3 node " " 'show ip mroute' ") + + source_i7 = topo["routers"]["i7"]["links"]["l1"]["ipv4"].split("/")[0] + source_i6 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + source_i5 = topo["routers"]["i5"]["links"]["c2"]["ipv4"].split("/")[0] + source_i3 = topo["routers"]["i3"]["links"]["r2"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"}, + { + "dut": "l1", + "src_address": source_i5, + "iif": "l1-c1-eth0", + "oil": "l1-i1-eth1", + }, + { + "dut": "l1", + "src_address": source_i3, + "iif": "l1-r2-eth4", + "oil": "l1-i1-eth1", + }, + { + "dut": "l1", + "src_address": source_i6, + "iif": "l1-i6-eth2", + "oil": "l1-i1-eth1", + }, + { + "dut": "l1", + "src_address": source_i7, + "iif": "l1-i7-eth3", + "oil": "l1-i1-eth1", + }, + {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"}, + { + "dut": "f1", + "src_address": source_i5, + "iif": "f1-c2-eth0", + "oil": "f1-i8-eth2", + }, + { + "dut": "f1", + "src_address": source_i3, + "iif": "f1-r2-eth3", + "oil": "f1-i8-eth2", + }, + { + "dut": "f1", + "src_address": source_i6, + "iif": "f1-r2-eth3", + "oil": "f1-i8-eth2", + }, + { + "dut": "f1", + "src_address": source_i7, + "iif": "f1-r2-eth3", + "oil": "f1-i8-eth2", + }, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Stop the source one by one on FRR1") + input_intf = {"i6": "i6-l1-eth0", "i7": "i7-l1-eth0"} + for dut, intf in input_intf.items(): + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + "After removing the source verify traffic is stopped" + " immediately and (S,G) got timeout in sometime" + ) + + logger.info("After shut, waiting for SG timeout") + + input_dict = [ + { + "dut": "l1", + "src_address": source_i6, + "iif": "l1-i6-eth2", + "oil": "l1-i1-eth1", + }, + { + "dut": "l1", + "src_address": source_i7, + "iif": "l1-i7-eth3", + "oil": "l1-i1-eth1", + }, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n mroutes are" + " still present \n Error: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + step( + "Source which is stopped got removed , other source" + " after still present verify using 'show ip mroute' " + ) + input_dict = [ + {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"}, + { + "dut": "l1", + "src_address": source_i5, + "iif": "l1-c1-eth0", + "oil": "l1-i1-eth1", + }, + { + "dut": "l1", + "src_address": source_i3, + "iif": "l1-r2-eth4", + "oil": "l1-i1-eth1", + }, + {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"}, + { + "dut": "f1", + "src_address": source_i5, + "iif": "f1-c2-eth0", + "oil": "f1-i8-eth2", + }, + { + "dut": "f1", + "src_address": source_i3, + "iif": "f1-r2-eth3", + "oil": "f1-i8-eth2", + }, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Start all the source again for all the receivers") + input_intf = {"i6": "i6-l1-eth0", "i7": "i7-l1-eth0"} + for dut, intf in input_intf.items(): + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "After starting source all the mroute entries got populated, " + "no duplicate entries present in mroute verify 'show ip mroute'" + ) + + input_dict = [ + {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"}, + { + "dut": "l1", + "src_address": source_i5, + "iif": "l1-c1-eth0", + "oil": "l1-i1-eth1", + }, + { + "dut": "l1", + "src_address": source_i3, + "iif": "l1-r2-eth4", + "oil": "l1-i1-eth1", + }, + { + "dut": "l1", + "src_address": source_i6, + "iif": "l1-i6-eth2", + "oil": "l1-i1-eth1", + }, + { + "dut": "l1", + "src_address": source_i7, + "iif": "l1-i7-eth3", + "oil": "l1-i1-eth1", + }, + {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"}, + { + "dut": "f1", + "src_address": source_i5, + "iif": "f1-c2-eth0", + "oil": "f1-i8-eth2", + }, + { + "dut": "f1", + "src_address": source_i3, + "iif": "f1-r2-eth3", + "oil": "f1-i8-eth2", + }, + { + "dut": "f1", + "src_address": source_i6, + "iif": "f1-r2-eth3", + "oil": "f1-i8-eth2", + }, + { + "dut": "f1", + "src_address": source_i7, + "iif": "f1-r2-eth3", + "oil": "f1-i8-eth2", + }, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_mroute_when_frr_is_transit_router_p2(request): + """ + TC_16: Verify (*,G) and (S,G) populated correctly + when FRR is the transit router + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP for (226.1.1.1-5) in c2") + input_dict = { + "c2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-5) to FRR1") + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to 225.1.1.1-5 receivers") + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, "i2", IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + # Stop r2 router to make r2 router disabled from topology + input_intf = {"l1": "l1-r2-eth4", "f1": "f1-r2-eth3"} + for dut, intf in input_intf.items(): + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + "FRR4 has (S,G) and (*,G) ,created where incoming interface" + " toward FRR3 and OIL toward R2, verify using 'show ip mroute'" + " 'show ip pim state' " + ) + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "c2", "src_address": "*", "iif": "lo", "oil": "c2-c1-eth0"}, + {"dut": "c2", "src_address": source, "iif": "c2-f1-eth1", "oil": "c2-c1-eth0"}, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"] + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Stop multicast traffic from FRR3") + dut = "i2" + intf = "i2-f1-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + + logger.info("Waiting for 20 sec to get traffic to be stopped..") + sleep(20) + + step("top IGMP receiver from FRR1") + dut = "i1" + intf = "i1-l1-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + + logger.info("Waiting for 20 sec to get mroutes to be flused out..") + sleep(20) + + step( + "After stopping receiver (*,G) also got timeout from transit" + " router 'show ip mroute'" + ) + + result = verify_ip_mroutes( + tgen, "c1", "*", IGMP_JOIN, "c1-c2-eth1", "c1-l1-eth0", expected=False + ) + assert result is not True, "Testcase {} : Failed \n mroutes are" + " still present \n Error: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_verify_mroute_when_RP_unreachable_p1(request): + """ + TC_23: Verify (S,G) should not create if RP is not reachable + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure RP on FRR2 (loopback interface) for " "the group range 225.0.0.0/8") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)") + + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to 225.1.1.1 receiver") + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure one IGMP interface on FRR3 node and send IGMP" " join (225.1.1.1)") + input_dict = { + "f1": {"igmp": {"interfaces": {"f1-i8-eth2": {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i8", "i8-f1-eth0", GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i8", IGMP_JOIN, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + # Verify mroutes are present in FRR3(f1) + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "f1", "src_address": "*", "iif": "f1-r2-eth3", "oil": "f1-i8-eth2"}, + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-i8-eth2"}, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"] + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the RP connected interface from f1 ( r2 to f1) link") + dut = "f1" + intf = "f1-r2-eth3" + shutdown_bringup_interface(tgen, dut, intf, False) + + logger.info("Waiting for 20 sec to get mroutes to be flushed out..") + sleep(20) + + step("Clear the mroute on f1") + clear_ip_mroute(tgen, "f1") + + step( + "After Shut the RP interface and clear the mroute verify all " + "(*,G) and (S,G) got timeout from FRR3 node , verify using " + " 'show ip mroute' " + ) + + result = verify_ip_mroutes( + tgen, "f1", "*", IGMP_JOIN, "f1-r2-eth3", "f1-i8-eth2", expected=False + ) + assert result is not True, "Testcase {} : Failed \n mroutes are" + " still present \n Error: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + step("IGMP groups are present verify using 'show ip igmp group'") + dut = "l1" + interface = "l1-i1-eth1" + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_igmp_query_timer_p0(request): + """ + TC_24: + Verify modification of IGMP query timer should get update + accordingly + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)") + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP on R2 (loopback interface) for the" " group range 225.0.0.0/8") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to 225.1.1.1 receiver") + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ip mroute' showing correct RPF and OIF" + " interface for (*,G) and (S,G) entries on all the nodes" + ) + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_4 = [ + {"dut": "l1", "src_address": "*", "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"}, + ] + for data in input_dict_4: + result = verify_ip_mroutes( + tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"] + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes" + ) + for data in input_dict_4: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Modify IGMP query interval default to other timer on FRR1" "3 times") + input_dict_1 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": {"igmp": {"query": {"query-interval": 100}}} + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": {"igmp": {"query": {"query-interval": 200}}} + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": {"igmp": {"query": {"query-interval": 300}}} + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_igmp_max_query_response_timer_p0(request): + """ + TC_25: + Verify modification of IGMP max query response timer + should get update accordingly + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)") + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure IGMP query response time to 10 sec on FRR1") + input_dict_1 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "version": "2", + "query": {"query-max-response-time": 10}, + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP on R2 (loopback interface) for the" " group range 225.0.0.0/8") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to 225.1.1.1 receiver") + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ip mroute' showing correct RPF and OIF" + " interface for (*,G) and (S,G) entries on all the nodes" + ) + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_5 = [ + {"dut": "l1", "src_address": "*", "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"}, + ] + for data in input_dict_5: + result = verify_ip_mroutes( + tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"] + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes" + ) + for data in input_dict_5: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Delete the PIM and IGMP on FRR1") + input_dict_1 = {"l1": {"pim": {"disable": ["l1-i1-eth1"]}}} + result = create_pim_config(tgen, topo, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "version": "2", + "delete": True, + "query": {"query-max-response-time": 10, "delete": True}, + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure PIM on FRR") + result = create_pim_config(tgen, topo["routers"]) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure max query response timer 100sec on FRR1") + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "version": "2", + "query": {"query-max-response-time": 100}, + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Remove and add max query response timer cli with different" + "timer 5 times on FRR1 Enable IGMP and IGMP version 2 on FRR1" + " on FRR1" + ) + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "version": "2", + "query": {"query-max-response-time": 110}, + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "version": "2", + "query": {"query-max-response-time": 120}, + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "version": "2", + "query": {"query-max-response-time": 140}, + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "version": "2", + "query": {"query-max-response-time": 150}, + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP and IGMP version 2 on FRR1 on FRR1") + + input_dict_4 = { + "l1": {"igmp": {"interfaces": {"l1-i1-eth1": {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict_4) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast-pim-sm-topo2/multicast_pim_sm_topo2.json b/tests/topotests/multicast-pim-sm-topo2/multicast_pim_sm_topo2.json new file mode 100644 index 0000000000..71454c2ab2 --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo2/multicast_pim_sm_topo2.json @@ -0,0 +1,140 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "routers": { + "l1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "i1": {"ipv4": "auto", "pim": "enable"}, + "i6": {"ipv4": "auto", "pim": "enable"}, + "i7": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"} + }, + "igmp": { + "interfaces": { + "l1-i1-eth1" :{ + "igmp":{ + "version": "2" + } + } + } + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.7.0/24", "10.0.6.0/24", "10.0.9.0/24"], + "next_hop": "10.0.12.2" + }, + { + "network": ["1.0.1.2/32", "1.0.3.5/32", "10.0.1.0/24", "1.0.2.2/32", "10.0.4.0/24", "10.0.3.0/24"], + "next_hop": "10.0.2.1" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i3": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.0.5.0/24", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24"], + "next_hop": "10.0.7.1" + }, + { + "network": ["1.0.1.2/32", "10.0.8.0/24", "10.0.10.0/24", "10.0.4.0/24", "10.0.0.0/24", "10.0.11.0/24", "10.0.1.0/24", "10.0.2.0/24"], + "next_hop": "10.0.12.1" + }] + }, + "f1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "i2": {"ipv4": "auto", "pim": "enable"}, + "i8": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.12.0/24", "10.0.11.0/24"], + "next_hop": "10.0.7.2" + }, + { + "network": ["1.0.2.2/32", "10.0.1.0/24", "10.0.0.0/24", "10.0.4.0/24", "1.0.1.2/32"], + "next_hop": "10.0.3.1" + }] + }, + "c1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "i4": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.6.0/24", "10.0.3.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.12.0/24", "10.0.10.0/24", "10.0.11.0/24"], + "next_hop": "10.0.2.2" + }, + { + "network": ["10.0.5.0/24", "10.0.7.0/24", "1.0.3.5/32", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24", "10.0.4.0/24"], + "next_hop": "10.0.0.2" + }] + }, + "c2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i5": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.7.0/24", "10.0.10.0/24", "10.0.11.0/24"], + "next_hop": "10.0.3.2" + }, + { + "network": ["1.0.1.2/32", "10.0.4.0/24", "10.0.2.0/24"], + "next_hop": "10.0.0.1" + }] + }, + "i1": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i2": { + "links": { + "f1": {"ipv4": "auto"} + } + }, + "i3": { + "links": { + "r2": {"ipv4": "auto"} + } + }, + "i4": { + "links": { + "c1": {"ipv4": "auto"} + } + }, + "i5": { + "links": { + "c2": {"ipv4": "auto"} + } + }, + "i6": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i7": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i8": { + "links": { + "f1": {"ipv4": "auto"} + } + } + } +} diff --git a/tests/topotests/multicast-pim-sm-topo2/test_multicast_pim_sm_topo2.py b/tests/topotests/multicast-pim-sm-topo2/test_multicast_pim_sm_topo2.py new file mode 100755 index 0000000000..a9d914da57 --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo2/test_multicast_pim_sm_topo2.py @@ -0,0 +1,1947 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +""" +Following tests are covered to test multicast pim sm: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: +1. TC_17: Verify (*,G) and (S,G) present and multicast traffic resume, + after restart of PIMd daemon +2. TC_18: Verify (*,G) and (S,G) present and multicast traffic resume after + FRR service stop and start +3. TC_10: Verify SPT switchover working when RPT and SPT path is + different +4. TC_15: Verify (S,G) and (*,G) mroute after shut / no shut of upstream + interfaces +5. TC_7: Verify mroute detail when receiver is present + outside of FRR +6. TC_8: Verify mroute when FRR is acting as FHR and LHR +7. TC_20: Verify mroute detail when 5 different receiver joining + same source +8. TC_22: Verify OIL and IIF detail updated in (S,G) mroute after shut + and no shut of the source interface +""" + +import os +import sys +import json +import time +import datetime +from time import sleep +import pytest + +pytestmark = pytest.mark.pimd + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + iperfSendIGMPJoin, + addKernelRoute, + reset_config_on_routers, + iperfSendTraffic, + kill_iperf, + shutdown_bringup_interface, + kill_router_daemons, + start_router, + start_router_daemons, + stop_router, + required_linux_kernel_version, + topo_daemons, +) +from lib.pim import ( + create_pim_config, + create_igmp_config, + verify_igmp_groups, + verify_ip_mroutes, + verify_pim_interface_traffic, + verify_upstream_iif, + verify_pim_neighbors, + verify_pim_state, + verify_ip_pim_join, + clear_ip_mroute, + clear_ip_pim_interface_traffic, + verify_igmp_config, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/multicast_pim_sm_topo2.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +TOPOLOGY = """ + + + i4-----c1-------------c2---i5 + | | + | | + i1-----l1------r2-----f1---i2 + | | | | + | | | | + i7 i6 i3 i8 + + Description: + i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP + join and traffic + l1 - LHR + f1 - FHR + r2 - FRR router + c1 - FRR router + c2 - FRR router +""" + +# Global variables +GROUP_RANGE = "225.0.0.0/8" +IGMP_JOIN = "225.1.1.1" +GROUP_RANGE_1 = [ + "225.1.1.1/32", + "225.1.1.2/32", + "225.1.1.3/32", + "225.1.1.4/32", + "225.1.1.5/32", +] +IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"] +GROUP_RANGE_2 = [ + "226.1.1.1/32", + "226.1.1.2/32", + "226.1.1.3/32", + "226.1.1.4/32", + "226.1.1.5/32", +] +IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"] + +GROUP_RANGE_3 = [ + "227.1.1.1/32", + "227.1.1.2/32", + "227.1.1.3/32", + "227.1.1.4/32", + "227.1.1.5/32", +] +IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"] + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.19") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, iperf, iperf_intf, GROUP_RANGE, join=False, traffic=False +): + """ + API to do pre-configuration to send IGMP join and multicast + traffic + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `iperf`: router running iperf + * `iperf_intf`: interface name router running iperf + * `GROUP_RANGE`: group range + * `join`: IGMP join, default False + * `traffic`: multicast traffic, default False + """ + + if join: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + if traffic: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + router_list = tgen.routers() + for router in router_list.keys(): + if router == iperf: + continue + + rnode = router_list[router] + rnode.run("echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter") + + return True + + +def verify_state_incremented(state_before, state_after): + """ + API to compare interface traffic state incrementing + + Parameters + ---------- + * `state_before` : State dictionary for any particular instance + * `state_after` : State dictionary for any particular instance + """ + + for router, state_data in state_before.items(): + for state, value in state_data.items(): + if state_before[router][state] >= state_after[router][state]: + errormsg = ( + "[DUT: %s]: state %s value has not" + " incremented, Initial value: %s, " + "Current value: %s [FAILED!!]" + % ( + router, + state, + state_before[router][state], + state_after[router][state], + ) + ) + return errormsg + + logger.info( + "[DUT: %s]: State %s value is " + "incremented, Initial value: %s, Current value: %s" + " [PASSED!!]", + router, + state, + state_before[router][state], + state_after[router][state], + ) + + return True + + +def test_verify_mroute_and_traffic_when_pimd_restarted_p2(request): + """ + TC_17: Verify (*,G) and (S,G) present and multicast traffic resume, + after restart of PIMd daemon + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP for (226.1.1.1-5) in c1") + step("Configure static RP for (232.1.1.1-5) in c2") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_dict = { + "c1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_2, + } + ] + } + }, + "c2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_3, + } + ] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Enable IGMP on FRR1 interface and send IGMP join " + "(226.1.1.1-5) and (232.1.1.1-5)" + ) + step( + "Configure IGMP interface on FRR3 and send IGMP join" + " for group (226.1.1.1-5, 232.1.1.1-5)" + ) + + input_dict = { + "f1": {"igmp": {"interfaces": {"f1-i8-eth2": {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i1": "i1-l1-eth0", "i8": "i8-f1-eth0"} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Connect one source to c2 and send multicast traffic all" + " the receivers (226.1.1.1-5, 232.1.1.1-5)" + ) + step( + "Send multicast traffic from FRR3 to all the receivers " + "(226.1.1.1-5, 232.1.1.1-5)" + ) + + input_src = {"i2": "i2-f1-eth0", "i5": "i5-c2-eth0"} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + # Verifying mroutes before PIMd restart, fetching uptime + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"}, + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-i8-eth2"}, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Restart Pimd process on FRR3 node") + kill_router_daemons(tgen, "f1", ["pimd"]) + start_router_daemons(tgen, "f1", ["pimd"]) + + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "After restart of PIMd verify pim nbr is up , IGMP groups" + " received , and (*,G) (S,G) entries populated again ," + " Verify using 'show ip pim neighbor' , 'show ip igmp groups'" + " 'show ip mroute'" + ) + + result = verify_pim_neighbors(tgen, topo) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dut = "f1" + interface = "f1-i8-eth2" + result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Stop the traffic and restart PIMd immediately on FRR3 node") + dut = "i2" + intf = "i2-f1-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + + kill_router_daemons(tgen, "f1", ["pimd"]) + start_router_daemons(tgen, "f1", ["pimd"]) + + step( + "After PIM process come , all the none of (S,G) mroute should" + " present on FRR3 'show ip mroute' " + ) + + input_dict = [ + {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"} + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + input_dict = [ + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "none"} + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n mroutes are" + " still present \n Error: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_verify_mroute_and_traffic_when_frr_restarted_p2(request): + """ + TC_18: Verify (*,G) and (S,G) present and multicast traffic resume after + FRR service stop and start + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP for (226.1.1.1-5) in c1") + step("Configure static RP for (232.1.1.1-5) in c2") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_dict = { + "c1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_2, + } + ] + } + }, + "c2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_3, + } + ] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Enable IGMP on FRR1 interface and send IGMP join " + "(226.1.1.1-5) and (232.1.1.1-5)" + ) + step( + "Configure IGMP interface on FRR3 and send IGMP join" + " for group (226.1.1.1-5, 232.1.1.1-5)" + ) + + input_dict = { + "f1": {"igmp": {"interfaces": {"f1-i8-eth2": {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i1": "i1-l1-eth0", "i8": "i8-f1-eth0"} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Connect one source to c2 and send multicast traffic all" + " the receivers (226.1.1.1-5, 232.1.1.1-5)" + ) + step( + "Send multicast traffic from FRR3 to all the receivers " + "(226.1.1.1-5, 232.1.1.1-5)" + ) + + input_src = {"i2": "i2-f1-eth0", "i5": "i5-c2-eth0"} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verifying mroutes before FRR restart, fetching uptime") + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"}, + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-i8-eth2"}, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Stop and Start the FRR services on FRR3 node") + stop_router(tgen, "f1") + start_router(tgen, "f1") + + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "After stop and start of FRR service verify pim nbr is up " + "IGMP groups received , and (*,G) (S,G) entries populated again" + " Verify using 'show ip pim neighbor' , 'show ip igmp groups'" + " 'show ip mroute'" + ) + + result = verify_pim_neighbors(tgen, topo) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dut = "f1" + interface = "f1-i8-eth2" + result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Stop the traffic and stop and start the FRR services on" " FRR3 node") + shutdown_bringup_interface(tgen, "i2", "i2-f1-eth0", False) + + stop_router(tgen, "f1") + start_router(tgen, "f1") + + step( + "After stop and start of FRR services , all the none of (S,G)" + " mroute should present on FRR3 node verify using " + "'show ip mroute'" + ) + + input_dict = [ + {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"} + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + input_dict = [ + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "none"} + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n mroutes are" + " still present \n Error: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_verify_SPT_switchover_when_RPT_and_SPT_path_is_different_p0(request): + """ + TC_10: Verify SPT switchover working when RPT and SPT path is + different + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP for (226.1.1.1-5) and " "(232.1.1.1-5) in c2") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_dict = { + "c2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": _GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Enable IGMP on FRR1 interface and send IGMP join " + "(226.1.1.1-5) and (232.1.1.1-5)" + ) + + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i1", "i1-l1-eth0", _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i1", _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to '226.1.1.1-5'" ", '232.1.1.1-5' receiver") + + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i2", "i2-f1-eth0", _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("registerRx and registerStopTx value before traffic sent") + state_dict = {"c2": {"c2-f1-eth1": ["registerRx", "registerStopTx"]}} + state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase {} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, "i2", _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify in FRR3 sending initial packet to RP using" + " 'show ip mroute' and mroute OIL is towards RP." + ) + + result = verify_ip_mroutes( + tgen, + "f1", + "10.0.5.2", + _IGMP_JOIN_RANGE, + "f1-i2-eth1", + ["f1-c2-eth0", "f1-r2-eth3"], + ) + assert result is True, "Testcase {} : " "Failed Error: {}".format(tc_name, result) + + result = verify_ip_mroutes( + tgen, "f1", "10.0.5.2", _IGMP_JOIN_RANGE, "f1-i2-eth1", "f1-r2-eth3" + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + " After spt switchover traffic is flowing between" + " (LHR(FRR1)-FHR(FRR3)) and (S,G) OIL is updated toward FRR1" + " 'show ip mroute' and 'show ip pim upstream'" + ) + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"}, + {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Stop the traffic to all the receivers") + + kill_iperf(tgen, "i2", "remove_traffic") + + step( + "Null register packet being send periodically from FRR3 to RP, " + "verify using show ip mroute on RP, have (S, G) entries null OIL" + " 'show ip mroute' and verify show ip pim interface traffic" + "(In RP Register msg should be received and Register stop should" + " be transmitted)" + ) + input_dict = [ + {"dut": "c2", "src_address": source, "iif": "c2-f1-eth1", "oil": "none"} + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"] + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("registerRx and registerStopTx value after traffic sent") + state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_after, dict + ), "Testcase {} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_mroute_after_shut_noshut_of_upstream_interface_p1(request): + """ + TC_15: Verify (S,G) and (*,G) mroute after shut / no shut of upstream + interfaces + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP for (226.1.1.1-5) in c1") + step("Configure static RP for (232.1.1.1-5) in c2") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_dict = { + "c1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_2, + } + ] + } + }, + "c2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_3, + } + ] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Enable IGMP on FRR1 interface and send IGMP join " + "(226.1.1.1-5) and (232.1.1.1-5)" + ) + step( + "Configure IGMP interface on FRR3 and send IGMP join" + " for group (226.1.1.1-5, 232.1.1.1-5)" + ) + + input_dict = { + "f1": {"igmp": {"interfaces": {"f1-i8-eth2": {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i1": "i1-l1-eth0", "i8": "i8-f1-eth0"} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Connect one source to c2 and send multicast traffic all" + " the receivers (226.1.1.1-5, 232.1.1.1-5)" + ) + step( + "Send multicast traffic from FRR3 to all the receivers " + "(226.1.1.1-5, 232.1.1.1-5)" + ) + + input_src = {"i2": "i2-f1-eth0", "i5": "i5-c2-eth0"} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "FRR3 (S,G) has one OIL for local receiver one toward c2" + " verify 'show ip mroute' and 'show ip pim upstream'" + ) + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"}, + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-i8-eth2"}, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_2 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut and No shut interface connected from FHR (FRR3)" " to c2") + dut = "f1" + intf = "f1-c2-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + shutdown_bringup_interface(tgen, dut, intf, True) + + step("Shut and No shut interface connected from LHR (FRR1)" " to c1") + dut = "l1" + intf = "l1-c1-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + shutdown_bringup_interface(tgen, dut, intf, True) + + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut and No shut FRR1 and FRR3 interface") + shutdown_bringup_interface(tgen, "l1", "l1-r2-eth4", False) + shutdown_bringup_interface(tgen, dut, intf, True) + + shutdown_bringup_interface(tgen, "f1", "f1-r2-eth3", False) + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "After shut/no shut of interface , verify traffic resume to all" + "the receivers (S,G) OIL update for all the receivers" + ) + + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Shut FRR1, FRR3 interface , clear mroute in FRR1" + " and No shut FRR1, FRR3 interface " + ) + dut = "l1" + intf = "l1-r2-eth4" + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "f1" + intf = "f1-r2-eth3" + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "l1" + intf = "l1-r2-eth4" + shutdown_bringup_interface(tgen, dut, intf, True) + + dut = "f1" + intf = "f1-r2-eth3" + shutdown_bringup_interface(tgen, dut, intf, True) + + clear_ip_mroute(tgen, "l1") + clear_ip_mroute(tgen, "l1") + + step( + "After no shut, verify traffic resume to all the receivers" + " (S,G) OIL update for all the receivers" + ) + + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Shut and no shut upstream interface from FRR1 to FRR2 and " + "cisco immediate after mroute/upstream got cleared" + ) + + dut = "l1" + intf_l1_r2 = "l1-r2-eth4" + shutdown_bringup_interface(tgen, dut, intf_l1_r2, False) + + intf_l1_c1 = "l1-c1-eth0" + shutdown_bringup_interface(tgen, dut, intf_l1_c1, False) + + done_flag = False + for retry in range(1, 11): + result = verify_upstream_iif(tgen, "l1", "Unknown", source, IGMP_JOIN_RANGE_2, + expected=False) + if result is not True: + done_flag = True + else: + continue + + if done_flag: + logger.info("Expected Behavior: {}".format(result)) + break + + assert done_flag is True, ( + "Testcase {} : Failed Error: \n " + "mroutes are still present, after waiting for 10 mins".format(tc_name) + ) + + step("No shut the Source interface just after the upstream is expired" " from FRR1") + shutdown_bringup_interface(tgen, dut, intf_l1_r2, True) + shutdown_bringup_interface(tgen, dut, intf_l1_c1, True) + + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Stop the traffic to all the receivers") + kill_iperf(tgen) + + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n mroutes are " + "still present \n Error: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_verify_mroute_when_receiver_is_outside_frr_p0(request): + """ + TC_7: Verify mroute detail when receiver is present + outside of FRR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP on c1 for group range " "(226.1.1.1-5) and (232.1.1.1-5)") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_dict = { + "c1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": _GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Enable IGMP on FRR1 interface and send IGMP join" + " (226.1.1.1-5) and (232.1.1.1-5)" + ) + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i1", "i1-l1-eth0", _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i1", _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Send multicast traffic from FRR3 to all the receivers " + "(226.1.1.1-5) and (232.1.1.1-5)" + ) + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i2", "i2-f1-eth0", _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, "i2", _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Configure one more receiver in c2 enable IGMP and send" + " join (226.1.1.1-5) and (232.1.1.1-5)" + ) + input_dict = { + "c2": {"igmp": {"interfaces": {"c2-i5-eth2": {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i5", "i5-c2-eth0", _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i5", _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("FRR1 has 10 (*.G) and 10 (S,G) verify using 'show ip mroute count'") + step( + "All the receiver are receiving traffic on FRR1 and (S,G) OIL is toward" + "receivers, verify using 'show ip mroute' 'show ip pim upstream'" + ) + step( + "All the receiver are receiving traffic on c2 and (S,G) OIL is " + "toward receivers, verify using 'show ip mroute' 'show ip pim upstream'" + ) + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"}, + {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + {"dut": "c2", "src_address": "*", "iif": "c2-c1-eth0", "oil": "c2-i5-eth2"}, + {"dut": "c2", "src_address": source, "iif": "c2-f1-eth1", "oil": "c2-i5-eth2"}, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "FRR3 has (S,G) OIL created toward c1/c2 receiver and FRR1 receiver" + "'show ip pim state'" + ) + input_dict = [ + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-c2-eth0"}, + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"}, + ] + for data in input_dict: + result = verify_pim_state( + tgen, + data["dut"], + data["iif"], + data["oil"], + _IGMP_JOIN_RANGE, + data["src_address"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_mroute_when_FRR_is_FHR_and_LHR_p0(request): + """ + TC_8: Verify mroute when FRR is acting as FHR and LHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP for group range (226.1.1.1-5) and " "(232.1.1.1-5) on c1") + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_dict = { + "c1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": _GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Enable IGMP on FRR1 interface and send IGMP join (226.1.1.1-5)" + " and (232.1.1.1-5)" + ) + step( + "Configure receiver on FRR3 with igmp and pim enabled and " + "send IGMP join (226.1.1.1-5) and (232.1.1.1-5)" + ) + step( + "Send multicast traffic from FRR3 to all the receivers " + "(226.1.1.1-5) and (232.1.1.1-5)" + ) + + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i1", "i1-l1-eth0", _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i2", "i2-f1-eth0", _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send IGMP join (226.1.1.1-5, 232.1.1.1-5) to LHR(l1)") + result = iperfSendIGMPJoin(tgen, "i1", _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to '226.1.1.1-5'" ", '232.1.1.1-5' receiver") + result = iperfSendTraffic(tgen, "i2", _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Configure receiver in f1 enable IGMP and send" + " join (226.1.1.1-5) and (232.1.1.1-5)" + ) + + step("Configure one IGMP interface on f1 node and send IGMP" " join (225.1.1.1)") + input_dict = { + "f1": {"igmp": {"interfaces": {"f1-i8-eth2": {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, "i8", "i8-f1-eth0", _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, "i8", _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + step( + "l1 and f1 has 10 IGMP groups (226.1.1.1-5, 232.1.1.1-5)," + " verify using 'show ip igmp groups'" + ) + dut = "l1" + interface = "l1-i1-eth1" + result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dut = "f1" + interface = "f1-i8-eth2" + result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "l1 , f1 has 10 (*,G) and 10 (S,G) for groups " + "(226.1.1.1-5, 232.1.1.1-5), verify using " + " 'show ip mroute'" + ) + + source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict = [ + {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"}, + {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"}, + {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"}, + {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Join timer is running in FHR and LHR , verify using" " 'show ip pim state'") + + for data in input_dict: + result = verify_pim_state( + tgen, + data["dut"], + data["iif"], + data["oil"], + _IGMP_JOIN_RANGE, + data["src_address"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + # Stop the multicast traffic + step("Stop the traffic to all the receivers") + kill_iperf(tgen) + + step( + "After traffic stopped , verify (*,G) entries are not flushed" + " out from FRR1 node verify using 'show ip mroute' " + ) + + input_dict = [ + {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"}, + {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"}, + ] + + done_flag = False + for retry in range(1, 11): + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + + if result is True: + done_flag = True + else: + continue + + if done_flag: + break + + assert done_flag is True, ( + "Testcase {} : Failed Error: \n " + "mroutes are still present, after waiting for 10 mins".format(tc_name) + ) + + step( + "After traffic stopped , verify (S,G) entries are flushed out" + " from FRR1 node verify using 'show ip mroute' " + ) + + input_dict = [ + {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + {"dut": "f1", "src_address": source, "iif": "i2-f1-eth0", "oil": "f1-r2-eth3"}, + ] + + done_flag = False + for retry in range(1, 11): + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False + ) + if result is not True: + done_flag = True + else: + continue + + if done_flag: + logger.info("Expected Behavior: {}".format(result)) + break + + assert done_flag is True, ( + "Testcase {} : Failed Error: \n " + "mroutes are still present, after waiting for 10 mins".format(tc_name) + ) + + write_test_footer(tc_name) + + +def test_verify_mroute_when_5_different_receiver_joining_same_sources_p0(request): + """ + TC_20: Verify mroute detail when 5 different receiver joining + same source + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP for (226.1.1.1-5) in c1") + step("Configure static RP for (232.1.1.1-5) in c2") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_dict = { + "c1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_2, + } + ] + } + }, + "c2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_3, + } + ] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Configure 2 IGMP interface on FRR1 and send IGMP join" + "for group (226.1.1.1-5, 232.1.1.1-5) from both the interface" + ) + step( + "Configure 2 IGMP interface on FRR3 and send IGMP join for" + " group (226.1.1.1-5, 232.1.1.1-5) from both the interface" + ) + step( + "Configure 1 IGMP interface on c2 and send IGMP join for" + "group (226.1.1.1-5, 232.1.1.1-5)" + ) + + input_dict = { + "f1": { + "igmp": { + "interfaces": { + "f1-i8-eth2": {"igmp": {"version": "2"}}, + "f1-i2-eth1": {"igmp": {"version": "2"}}, + } + } + }, + "l1": {"igmp": {"interfaces": {"l1-i6-eth2": {"igmp": {"version": "2"}}}}}, + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = { + "i1": "i1-l1-eth0", + "i6": "i6-l1-eth0", + "i8": "i8-f1-eth0", + "i2": "i2-f1-eth0", + } + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + step("Configure one source in FRR2 , one in c1") + step( + "Send multicast traffic from both the sources to all the" + "receivers (226.1.1.1-5, 232.1.1.1-5)" + ) + + input_src = {"i3": "i3-r2-eth0"} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + step( + "After all the IGMP groups received with correct port using" + " 'show ip igmp groups' in FRR1, FRR3, c2" + ) + dut = "l1" + interface = "l1-i6-eth2" + result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dut = "f1" + interface = "f1-i8-eth2" + result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "(*,G) entries got created with upstream interface RP connected" + " port using 'show ip pim upstream' in FRR1, FRR3, c2" + ) + step( + "(S,G) entries created for all the receiver after starting the" + " source , traffic is reaching to all the receiver , verify OIL" + " of (S,G) is receiver port using 'show ip mroute' in FRR1, " + "FRR3 c2" + ) + + source = topo["routers"]["i3"]["links"]["r2"]["ipv4"].split("/")[0] + input_dict_all = [ + {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}, + {"dut": "f1", "src_address": source, "iif": "f1-r2-eth3", "oil": "f1-i8-eth2"}, + ] + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the receiver interface one by one on FRR1 node") + shutdown_bringup_interface(tgen, "l1", "l1-i1-eth1", False) + shutdown_bringup_interface(tgen, "l1", "l1-i6-eth2", False) + + step( + "After shut the receiver port verify traffic is stopped immediately" + " and (S,G) got timeout immediately in FRR1, FRR3, c2" + ) + input_dict = [ + {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"} + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n mroutes are" + " still present \n Error: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + step( + "No traffic impact observed on other receivers verify using" + " 'show ip mroute' " + ) + input_dict = [ + {"dut": "f1", "src_address": source, "iif": "f1-r2-eth3", "oil": "f1-i8-eth2"} + ] + for data in input_dict: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("No shut the receiver interface one by one on FRR1 node") + shutdown_bringup_interface(tgen, "l1", "l1-i1-eth1", True) + shutdown_bringup_interface(tgen, "l1", "l1-i6-eth2", True) + + step( + "After no shut of receivers all the mroute entries got populated" + ", no duplicate entries present in mroute" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_oil_iif_for_mroute_after_shut_noshut_source_interface_p1(request): + """ + TC_22: Verify OIL and IIF detail updated in (S,G) mroute after shut + and no shut of the source interface + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP for (226.1.1.1-5) in c1") + step("Configure static RP for (232.1.1.1-5) in c2") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_dict = { + "c1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_2, + } + ] + } + }, + "c2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_3, + } + ] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Configure IGMP interface on FRR1 and FRR3 and send IGMP join" + " for group (226.1.1.1-5, 232.1.1.1-5)" + ) + + input_dict = { + "f1": {"igmp": {"interfaces": {"f1-i8-eth2": {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i1": "i1-l1-eth0", "i8": "i8-f1-eth0"} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure 1 source in FRR1 , 1 in FRR3") + step( + "Send multicast traffic from both the sources to all the " + "receivers (226.1.1.1-5, 232.1.1.1-5)" + ) + + input_src = {"i6": "i6-l1-eth0", "i2": "i2-f1-eth0"} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "*,G) is created and (S,G) created on FRR1 and FRR3 for both" + " the source verify using 'show ip mroute' and " + " 'show ip pim upstream' to check the upstream interface" + " details" + ) + + source_i6 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"}, + { + "dut": "l1", + "src_address": source_i2, + "iif": "l1-r2-eth4", + "oil": "l1-i1-eth1", + }, + { + "dut": "l1", + "src_address": source_i6, + "iif": "l1-i6-eth2", + "oil": "l1-i1-eth1", + }, + {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"}, + { + "dut": "f1", + "src_address": source_i2, + "iif": "f1-i2-eth1", + "oil": "f1-i8-eth2", + }, + { + "dut": "f1", + "src_address": source_i6, + "iif": "f1-r2-eth3", + "oil": "f1-i8-eth2", + }, + ] + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the source interface one by one on FRR1") + shutdown_bringup_interface(tgen, "f1", "f1-i2-eth1", False) + + step( + "After shut of ource interface from FRR3 verify all the (S,G) " + "entries flushed out from FRR3 node 'show ip pim upstream' " + " 'show ip mroute' " + ) + + result = verify_ip_mroutes( + tgen, + "f1", + source_i2, + _IGMP_JOIN_RANGE, + "f1-i2-eth1", + "f1-i8-eth2", + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n mroutes are" + " still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behavior: {}".format(result)) + + result = verify_upstream_iif( + tgen, "f1", "Unknown", "10.0.5.2", _IGMP_JOIN_RANGE, joinState="NotJoined" + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo3.json b/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo3.json new file mode 100644 index 0000000000..f582f4929d --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo3.json @@ -0,0 +1,140 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "routers": { + "l1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "i1": {"ipv4": "auto", "pim": "enable"}, + "i6": {"ipv4": "auto", "pim": "enable"}, + "i7": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"} + }, + "igmp": { + "interfaces": { + "l1-i1-eth1" :{ + "igmp":{ + "version": "2" + } + } + } + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.9.0/24", "1.0.3.5/32"], + "next_hop": "10.0.12.2" + }, + { + "network": ["1.0.1.2/32", "1.0.3.5/32", "10.0.1.0/24", "1.0.2.2/32", "10.0.4.0/24"], + "next_hop": "10.0.2.1" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i3": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.0.5.0/24", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24", "1.0.3.5/32"], + "next_hop": "10.0.7.1" + }, + { + "network": ["1.0.1.2/32", "10.0.8.0/24", "10.0.10.0/24", "10.0.4.0/24", "10.0.11.0/24", "10.0.1.0/24"], + "next_hop": "10.0.12.1" + }] + }, + "f1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "i2": {"ipv4": "auto", "pim": "enable"}, + "i8": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24", "10.0.12.0/24"], + "next_hop": "10.0.7.2" + }, + { + "network": ["1.0.2.2/32", "10.0.1.0/24", "10.0.4.0/24", "1.0.1.2/32"], + "next_hop": "10.0.3.1" + }] + }, + "c1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "i4": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.6.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24"], + "next_hop": "10.0.2.2" + }, + { + "network": ["10.0.5.0/24", "10.0.7.0/24", "1.0.3.5/32", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24", "10.0.4.0/24"], + "next_hop": "10.0.0.2" + }] + }, + "c2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i5": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24"], + "next_hop": "10.0.3.2" + }, + { + "network": ["1.0.1.2/32", "10.0.4.0/24"], + "next_hop": "10.0.0.1" + }] + }, + "i1": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i2": { + "links": { + "f1": {"ipv4": "auto"} + } + }, + "i3": { + "links": { + "r2": {"ipv4": "auto"} + } + }, + "i4": { + "links": { + "c1": {"ipv4": "auto"} + } + }, + "i5": { + "links": { + "c2": {"ipv4": "auto"} + } + }, + "i6": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i7": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i8": { + "links": { + "f1": {"ipv4": "auto"} + } + } + } +} diff --git a/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo4.json b/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo4.json new file mode 100644 index 0000000000..4635dac7d2 --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo4.json @@ -0,0 +1,137 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "routers": { + "l1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "i1": {"ipv4": "auto", "pim": "enable"}, + "i6": {"ipv4": "auto", "pim": "enable"}, + "i7": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"} + }, + "igmp": { + "interfaces": { + "l1-i1-eth1" :{ + "igmp":{ + "version": "2" + } + } + } + }, + "static_routes": [{ + "network": ["10.0.4.0/24", "10.0.3.1/24"], + "next_hop": "10.0.12.2" + }, + { + "network": ["10.0.1.2/24"], + "next_hop": "10.0.2.1" + }] + + }, + + "r2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i3": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.0.4.0/24","10.0.3.1/24"], + "next_hop": "10.0.7.1" + }, + { + "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"], + "next_hop": "10.0.12.1" + }] + }, + "f1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "i2": {"ipv4": "auto", "pim": "enable"}, + "i8": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.0.4.0/24","10.0.3.1/24"], + "next_hop": "10.0.3.1" + }, + { + "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"], + "next_hop": "10.0.7.2" + }] + }, + "c1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "i4": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.4.11/32","10.0.4.2/24", "10.0.3.1/24"], + "next_hop": "10.0.2.2" + }] + + + }, + "c2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i5": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [ + { + "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"], + "next_hop": "10.0.3.2" + }] + }, + "i1": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i2": { + "links": { + "f1": {"ipv4": "auto"} + } + }, + "i3": { + "links": { + "r2": {"ipv4": "auto"} + } + }, + "i4": { + "links": { + "c1": {"ipv4": "auto"} + } + }, + "i5": { + "links": { + "c2": {"ipv4": "auto"} + } + }, + "i6": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i7": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i8": { + "links": { + "f1": {"ipv4": "auto"} + } + } + } +} diff --git a/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py new file mode 100755 index 0000000000..fdceb77fd1 --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py @@ -0,0 +1,4609 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +""" +Following tests are covered to test multicast pim sm: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: + +1. verify oil when join prune sent scenario_1 p0 +2. verify oil when join prune sent scenario_2 p0 +3. shut noshut source interface when upstream cleared from LHR p0( +4. shut noshut receiver interface when upstream cleared from LHR p0( +5. verify igmp clis p0 +6. verify igmp cli generate query once p0 +7. verify remove add igmp config to receiver interface p0 +8. verify remove add igmp commands when pim configured p0 +9. verify remove add pim commands when igmp configured p0 +10. pim dr priority p0 +11. pim hello timer p0 +12. Verify mroute after removing RP sending IGMP prune p2 +13. Verify prune is sent to LHR and FHR when PIM nbr went down +14. Verify mroute flag in LHR and FHR node +15. Verify IGMP prune processed correctly when same join received from IGMP and PIM +16. Verify multicast traffic flowing fine, when LHR connected to RP +17. Verify multicast traffic is flowing fine when FHR is connected to RP +""" + +import os +import re +import sys +import json +import time +import datetime +import pytest + +pytestmark = pytest.mark.pimd + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + iperfSendIGMPJoin, + addKernelRoute, + reset_config_on_routers, + iperfSendTraffic, + kill_iperf, + shutdown_bringup_interface, + kill_router_daemons, + start_router, + start_router_daemons, + stop_router, + apply_raw_config, + add_interfaces_to_vlan, + tcpdump_capture_start, + tcpdump_capture_stop, + LOGDIR, + check_router_status, + required_linux_kernel_version, + topo_daemons, +) +from lib.pim import ( + create_pim_config, + create_igmp_config, + verify_igmp_groups, + verify_ip_mroutes, + clear_ip_mroute_verify, + clear_ip_mroute, + clear_ip_pim_interface_traffic, + verify_igmp_config, + verify_pim_neighbors, + verify_pim_config, + verify_pim_interface, + verify_upstream_iif, + verify_multicast_traffic, + verify_pim_rp_info, + get_refCount_for_mroute, + verify_multicast_flag_state, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/multicast_pim_sm_topo3.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +TOPOLOGY = """ + + i4-----c1-------------c2---i5 + | | + | | + i1-----l1------r2-----f1---i2 + | | | | + | | | | + i7 i6 i3 i8 + + Description: + i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP + join and traffic + l1 - LHR + f1 - FHR + r2 - FRR router + c1 - FRR router + c2 - FRR router +""" + +# Global variables +VLAN_1 = 2501 +GROUP_RANGE = "225.0.0.0/8" +IGMP_GROUP = "225.1.1.1/32" +IGMP_JOIN = "225.1.1.1" +VLAN_INTF_ADRESS_1 = "10.0.8.3/24" +GROUP_RANGE_1 = [ + "225.1.1.1/32", + "225.1.1.2/32", + "225.1.1.3/32", + "225.1.1.4/32", + "225.1.1.5/32", +] +IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"] +GROUP_RANGE_2 = [ + "226.1.1.1/32", + "226.1.1.2/32", + "226.1.1.3/32", + "226.1.1.4/32", + "226.1.1.5/32", +] +IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"] +GROUP_RANGE_3 = [ + "227.1.1.1/32", + "227.1.1.2/32", + "227.1.1.3/32", + "227.1.1.4/32", + "227.1.1.5/32", +] +IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"] + +SAME_VLAN_IP_1 = {"ip": "10.1.1.1", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_2 = {"ip": "10.1.1.2", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_3 = {"ip": "10.1.1.3", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_4 = {"ip": "10.1.1.4", "subnet": "255.255.255.0", "cidr": "24"} +TCPDUMP_FILE = "{}/{}".format(LOGDIR, "v2query.txt") + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.19") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, iperf, iperf_intf, GROUP_RANGE, join=False, traffic=False +): + """ + API to do pre-configuration to send IGMP join and multicast + traffic + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `iperf`: router running iperf + * `iperf_intf`: interface name router running iperf + * `GROUP_RANGE`: group range + * `join`: IGMP join, default False + * `traffic`: multicast traffic, default False + """ + + if join: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + if traffic: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + router_list = tgen.routers() + for router in router_list.keys(): + if router == iperf: + continue + + rnode = router_list[router] + rnode.run("echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter") + + for router in topo["routers"].keys(): + if "static_routes" in topo["routers"][router]: + static_routes = topo["routers"][router]["static_routes"] + for static_route in static_routes: + network = static_route["network"] + next_hop = static_route["next_hop"] + if type(network) is not list: + network = [network] + for net in network: + addKernelRoute(tgen, router, iperf_intf, net, next_hop) + return True + + +def verify_mroute_repopulated(uptime_before, uptime_after): + """ + API to compare uptime for mroutes + + Parameters + ---------- + * `uptime_before` : Uptime dictionary for any particular instance + * `uptime_after` : Uptime dictionary for any particular instance + """ + + for group in uptime_before.keys(): + for source in uptime_before[group].keys(): + if set(uptime_before[group]) != set(uptime_after[group]): + errormsg = ( + "mroute (%s, %s) has not come" + " up after mroute clear [FAILED!!]" % (source, group) + ) + return errormsg + + d1 = datetime.datetime.strptime(uptime_before[group][source], "%H:%M:%S") + d2 = datetime.datetime.strptime(uptime_after[group][source], "%H:%M:%S") + if d2 >= d1: + errormsg = "mroute (%s, %s) is not " "repopulated [FAILED!!]" % ( + source, + group, + ) + return errormsg + + logger.info("mroute (%s, %s) is " "repopulated [PASSED!!]", source, group) + + return True + + +def verify_state_incremented(state_before, state_after): + """ + API to compare interface traffic state incrementing + + Parameters + ---------- + * `state_before` : State dictionary for any particular instance + * `state_after` : State dictionary for any particular instance + """ + + for router, state_data in state_before.items(): + for state, value in state_data.items(): + if state_before[router][state] >= state_after[router][state]: + errormsg = ( + "[DUT: %s]: state %s value has not" + " incremented, Initial value: %s, " + "Current value: %s [FAILED!!]" + % ( + router, + state, + state_before[router][state], + state_after[router][state], + ) + ) + return errormsg + + logger.info( + "[DUT: %s]: State %s value is " + "incremented, Initial value: %s, Current value: %s" + " [PASSED!!]", + router, + state, + state_before[router][state], + state_after[router][state], + ) + + return True + + +def find_v2_query_msg_in_tcpdump(tgen, router, message, count, cap_file): + """ + Find v2 query messages in tcpdump file + + Parameters + ---------- + * `tgen` : Topology handler + * `router` : Device under test + * `cap_file` : tcp dump file name + + """ + + filepath = os.path.join(LOGDIR, tgen.modname, router, cap_file) + with open(filepath) as f: + if len(re.findall("{}".format(message), f.read())) < count: + errormsg = "[DUT: %s]: Verify Message: %s in tcpdump" " [FAILED!!]" % ( + router, + message, + ) + return errormsg + + logger.info( + "[DUT: %s]: Found message: %s in tcpdump " " count: %s [PASSED!!]", + router, + message, + count, + ) + return True + + +def find_tos_in_tcpdump(tgen, router, message, cap_file): + """ + Find v2 query messages in tcpdump file + + Parameters + ---------- + * `tgen` : Topology handler + * `router` : Device under test + * `cap_file` : tcp dump file name + + """ + + filepath = os.path.join(LOGDIR, tgen.modname, router, cap_file) + with open(filepath) as f: + + if len(re.findall(message, f.read())) < 1: + errormsg = "[DUT: %s]: Verify Message: %s in tcpdump" " [FAILED!!]" % ( + router, + message, + ) + return errormsg + + logger.info( + "[DUT: %s]: Found message: %s in tcpdump " "[PASSED!!]", router, message + ) + return True + + +def test_verify_oil_when_join_prune_sent_scenario_1_p1(request): + """ + TC_21_1: + Verify OIL detail updated in (S,G) and (*,G) mroute when IGMP + join/prune is sent + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (226.1.1.1-5)" + ) + step( + "Enable IGMP of FRR3 interface and send IGMP joins " + " from FRR3 node for group range (226.1.1.1-5)" + ) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_dict = { + "f1": {"igmp": {"interfaces": {intf_f1_i8: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = { + "i1": topo["routers"]["i1"]["links"]["l1"]["interface"], + "i8": topo["routers"]["i8"]["links"]["f1"]["interface"], + } + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (226.1.1.1-5) in R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Configure one source on FRR3 for all the groups and send" " multicast traffic" + ) + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i2, + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " "FRR1 node") + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False) + + step( + "After receiving the IGMP prune from FRR1 , verify traffic " + "immediately stopped for this receiver 'show ip multicast'" + ) + + input_traffic = {"l1": {"traffic_sent": [intf_l1_i1]}} + result = verify_multicast_traffic(tgen, input_traffic, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + " Traffic is not stopped yet \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step( + "IGMP groups are remove from FRR1 node 'show ip igmp groups'" + " FRR3 IGMP still present" + ) + + dut = "l1" + result = verify_igmp_groups( + tgen, dut, intf_l1_i1, IGMP_JOIN_RANGE_1, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "IGMP groups are not deleted \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + dut = "f1" + result = verify_igmp_groups(tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "(*,G) and (S,G) OIL got removed immediately after receiving" + " prune 'show ip pim state' and 'show ip mroute' on FRR1 node," + " no impact on FRR3 receiver" + ) + + input_dict_l1 = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroutes are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "upstream entries are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + input_dict_f1 = [ + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_f1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_f1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " " FRR3 node") + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + shutdown_bringup_interface(tgen, "f1", intf_f1_i8, False) + + step( + "After receiving the IGMP prune from FRR3s , verify traffic " + "immediately stopped for this receiver 'show ip multicast'" + ) + + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + result = verify_multicast_traffic(tgen, input_traffic, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + " Traffic is not stopped yet \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step( + "IGMP groups are remove from FRR1 node 'show ip igmp groups'" + " FRR3 IGMP still present" + ) + + dut = "f1" + result = verify_igmp_groups( + tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "IGMP groups are not deleted \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step( + "(*,G) and (S,G) OIL got prune state (none) from all the nodes" + "FRR1, FRR3 verify using 'show ip mroute'" + ) + + input_dict_l1 = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroutes are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "upstream entries are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + shutdown_bringup_interface(tgen, "f1", intf_f1_i8, True) + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True) + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_oil_when_join_prune_sent_scenario_2_p1(request): + """ + TC_21_2: Verify OIL detail updated in (S,G) and (*,G) mroute when IGMP + join/prune is sent + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Removing FRR3 to simulate topo " "FHR(FRR1)---LHR(FRR2)") + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_r2, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (226.1.1.1-5)" + ) + step( + "Enable IGMP of FRR3 interface and send IGMP joins " + " from FRR3 node for group range (226.1.1.1-5)" + ) + + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + input_dict = { + "r2": {"igmp": {"interfaces": {intf_r2_i3: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = { + "i1": topo["routers"]["i1"]["links"]["l1"]["interface"], + "i3": topo["routers"]["i3"]["links"]["r2"]["interface"], + } + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (226.1.1.1-5) in R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " "FRR3(r2) node") + + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_i3, False) + + step( + "After sending IGMP prune from FRR3(r2) node verify (*,G) OIL " + "immediately removed for local receiver mroute should have " + " PIM protocol , IGMP should be removed verify using " + "'show ip mroute' no impact seen on FRR1(l1) (*,G)" + ) + + input_dict_r2 = [ + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + } + ] + + for data in input_dict_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroutes are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + input_dict_l1_r2 = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_l1_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " "FRR1(l1) node") + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False) + + step( + "After sending IGMP prune from FRR1 node verify (*,G) OIL" + "got removed immediately from FRR1 node" + ) + + input_dict_l1 = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroutes are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("After prune is sent verify upstream got removed in FRR1 node") + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "upstream entries are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + write_test_footer(tc_name) + + +def test_shut_noshut_source_interface_when_upstream_cleared_from_LHR_p1(request): + """ + TC_26: Verify shut/no shut of source interface after upstream got cleared + from LHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP on R2 (loopback interface) for " "the group range 225.0.0.0/8") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to 225.1.1.1-225.1.1.10" " receiver") + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "'show ip mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i2, + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + step( + "'show ip pim upstream' and 'show ip pim upstream-rpf' showing" + " correct OIL and IIF on all the nodes" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the source interface from FRR3") + intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"] + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, False) + + step( + "After shut of source interface verify (S,G) mroutes are cleared" + " from all the nodes" + ) + + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + result = verify_ip_mroutes( + tgen, "f1", source_i2, IGMP_JOIN_RANGE_1, intf_f1_i2, intf_f1_r2, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n mroutes are" + " still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behavior: {}".format(result)) + + step( + "After waiting for (S,G) timeout from FRR1 for same" + " source verify that (S,G) is flushed from FRR1 node" + " 'show ip pim upstream' 'show ip mroute' " + ) + + done_flag = False + for retry in range(1, 11): + result = verify_upstream_iif( + tgen, "l1", "Unknown", source_i2, IGMP_JOIN_RANGE_1, expected=False + ) + if result is not True: + done_flag = True + else: + continue + if done_flag: + logger.info("Expected Behavior: {}".format(result)) + break + + assert done_flag is True, ( + "Testcase {} : Failed Error: \n " + "mroutes are still present, after waiting for 10 mins".format(tc_name) + ) + + step("No shut the Source interface just after the upstream is expired" " from FRR1") + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, True) + + step( + "After no shut of source interface , verify all the (S,G) is " + " populated again on 'show ip mroute' 'show ip pim upstream' " + " with proper OIL and IIF detail" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("shut and no shut the source interface immediately") + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, True) + + step( + "All the mroutes got updated with proper OIL after no shut of" + "interface verify using 'show ip mroute'" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_shut_noshut_receiver_interface_when_upstream_cleared_from_LHR_p1(request): + """ + TC_27: Verify shut/no shut of receiver interface after upstream got + cleared from LHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP on R2 (loopback interface) for " "the group range 225.0.0.0/8") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to 225.1.1.1-225.1.1.10" " receiver") + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "'show ip mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i2, + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "'show ip pim upstream' and 'show ip pim upstream-rpf' showing" + " correct OIL and IIF on all the nodes" + ) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the source interface FRR1") + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"] + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False) + + step( + "After waiting for (S,G) timeout from FRR1 for same" + " source verify that (S,G) is flushed from FRR1 node" + " 'show ip pim upstream' 'show ip mroute' " + ) + + done_flag = False + for retry in range(1, 11): + result = verify_upstream_iif( + tgen, "l1", "Unknown", source_i2, IGMP_JOIN_RANGE_1, expected=False + ) + if result is not True: + done_flag = True + else: + continue + if done_flag: + logger.info("Expected Behavior: {}".format(result)) + break + + assert done_flag is True, ( + "Testcase {} : Failed Error: \n " + "mroutes are still present, after waiting for 10 mins".format(tc_name) + ) + + step("No shut the Source interface just after the upstream is expired" " from FRR1") + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True) + + step( + "After no shut of source interface , verify all the (S,G) is " + " populated again on 'show ip mroute' 'show ip pim upstream' " + " with proper OIL and IIF detail" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("shut and no shut the source interface immediately") + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, True) + + step( + "After no shut of receiver interface , verify all the (S,G) is " + "populated again on 'show ip mroute' 'show ip pim upstream' " + "with proper OIL and IIF detail" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_remove_add_igmp_config_to_receiver_interface_p0(request): + """ + TC_33: Verify removing and adding IGMP config from the receiver interface + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable PIM on all routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure source on FRR3 and start the traffic for" " (225.1.1.1-225.1.1.10)") + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure source on FRR1 and start the traffic for" " (225.1.1.1-225.1.1.10)") + + input_src = {"i6": topo["routers"]["i6"]["links"]["l1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i6 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i6, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from" + " receiver interface of FRR1" + ) + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": {intf_l1_i1: {"igmp": {"version": "2", "delete": True,}}} + } + } + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("IGMP join removed from FRR1 , verify using " "'show ip igmp groups json'") + + dut = "l1" + interface = topo["routers"]["l1"]["links"]["i1"]["interface"] + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Groups are not" + " present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"] + input_traffic = { + "l1": {"traffic_received": [intf_l1_r2], "traffic_sent": [intf_l1_i1]}, + "f1": {"traffic_sent": [intf_f1_r2], "traffic_received": [intf_f1_i2]}, + } + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Configure igmp 'ip igmp' and 'ip igmp version 2' from " + "receiver interface of FRR1" + ) + + input_dict_2 = { + "l1": {"igmp": {"interfaces": {intf_l1_i1: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "After adding IGMP on receiver interface verify (S,G) and (*,G)" + " entries got populated and traffic is resumed on FRR1 and FRR3 node" + ) + + step( + "Verify OIL/IIF and drJoinDesired using 'show ip mroute , and traffic" + " using show ip pim upstream and show ip multicast'" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from" + " receiver interface of FRR1" + ) + + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": {intf_l1_i1: {"igmp": {"version": "2", "delete": True,}}} + } + } + } + + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("IGMP join removed from FRR1 , verify using " "'show ip igmp groups json'") + + dut = "l1" + interface = topo["routers"]["l1"]["links"]["i1"]["interface"] + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Groups are not" + " present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Configure igmp 'ip igmp' and 'ip igmp version 2' from " + "receiver interface of FRR1" + ) + + input_dict_2 = { + "l1": {"igmp": {"interfaces": {intf_l1_i1: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "After adding IGMP on receiver interface verify (S,G) and (*,G)" + " entries got populated and traffic is resumed on FRR1 and FRR3 node" + ) + + step( + "Verify OIL/IIF and drJoinDesired using 'show ip mroute , and traffic" + " using show ip pim upstream and show ip multicast'" + ) + + input_dict_l1_f1 = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i6, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_l1_f1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_l1_f1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Remove ip igmp and send igmp prune from FRR1 interface") + + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": {intf_l1_i1: {"igmp": {"version": "2", "delete": True,}}} + } + } + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + step( + "Verification: After removing igmp 'no ip igmp' and " + " sending prune verify mroute and upstream got removed" + " from FRR1 verify using 'show ip mroute' and " + "'show ip pim upstream'" + ) + + dut = "l1" + iif = topo["routers"]["l1"]["links"]["i6"]["interface"] + oil = topo["routers"]["l1"]["links"]["i1"]["interface"] + source = source_i6 + result = verify_ip_mroutes( + tgen, dut, source, IGMP_JOIN_RANGE_1, iif, oil, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n routes are still" + " present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + write_test_footer(tc_name) + + +def test_verify_remove_add_igmp_commands_when_pim_configured_p0(request): + """ + TC_34: Verify removing and adding IGMP commands when PIM is already + configured + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable PIM on all routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure source on FRR3 and start the traffic for" " (225.1.1.1-225.1.1.10)") + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure source on FRR1 and start the traffic for" " (225.1.1.1-225.1.1.10)") + + input_src = {"i6": topo["routers"]["i6"]["links"]["l1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i6 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i6, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verification: After configuring IGMP related config , " + "verify config is present in the interface " + "'show ip igmp interface ensxx json'" + ) + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + input_dict_1 = { + "l1": {"igmp": {"interfaces": {intf_l1_i1: {"igmp": {"version": "2"}}}}} + } + + result = verify_igmp_config(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from" + " receiver interface of FRR1" + ) + + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": {intf_l1_i1: {"igmp": {"version": "2", "delete": True,}}} + } + } + } + + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Verification: After removing the config CLI got removed " + "'show ip igmp interface ensxx json'" + ) + + result = verify_igmp_config(tgen, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "IGMP interface is not removed \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure 'ip igmp last-member-query-count 10' on FRR1" " receiver interface") + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": {"igmp": {"query": {"last-member-query-count": 5}}} + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Remove 'ip igmp last-member-query-count 10' on FRR1" " receiver interface") + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "query": {"last-member-query-count": "", "delete": True} + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": {"igmp": {"query": {"last-member-query-count": 2}}} + } + } + } + } + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Configure 'ip igmp last-member-query-interval 20' on FRR1" + " receiver interface" + ) + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": {"query": {"last-member-query-interval": 20}} + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Remove 'ip igmp last-member-query-count 10' on FRR1" " receiver interface") + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "query": {"last-member-query-interval": "", "delete": True} + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": {"query": {"last-member-query-interval": 10}} + } + } + } + } + } + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_remove_add_pim_commands_when_igmp_configured_p1(request): + """ + TC_35: Verify removing and adding PIM commands when IGMP is already + configured + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure 'ip pim' on receiver interface on FRR1") + step("Enable PIM on all routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim' on receiver interface on FRR1") + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + input_dict_1 = {"l1": {"pim": {"disable": intf_l1_i1}}} + result = create_pim_config(tgen, topo, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim bsm' on receiver interface on FRR1") + + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "ip pim bsm"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim bsm' on receiver interface on FRR1") + + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "no ip pim bsm"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim drpriority' on receiver interface on FRR1") + + raw_config = { + "l1": { + "raw_config": ["interface {}".format(intf_l1_i1), "ip pim drpriority 10"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verification: After configuring PIM related config, " + "verify config is present in the interface " + "'show ip pim interface ensxx json'" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"drPriority": 10}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim drpriority' on receiver interface on FRR1") + + raw_config = { + "l1": { + "raw_config": ["interface {}".format(intf_l1_i1), "no ip pim drpriority 10"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verification: After removing the config CLI got removed " + "'show ip pim interface ensxx json'" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"drPriority": 1}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim hello' on receiver interface on FRR1") + + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "ip pim hello 50"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verification: After configuring PIM related config, " + "verify config is present in the interface " + "'show ip pim interface ensxx json'" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"helloPeriod": 50}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim hello' on receiver interface on FRR1") + + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "no ip pim hello"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verification: After removing the config CLI got removed " + "'show ip pim interface ensxx json'" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"helloPeriod": 30}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim unicast-bsm' on receiver interface on FRR1") + + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "ip pim unicast-bsm"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim hello' on receiver interface on FRR1") + + raw_config = { + "l1": { + "raw_config": ["interface {}".format(intf_l1_i1), "no ip pim unicast-bsm"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_pim_dr_priority_p0(request): + """ + TC_36: Verify highest DR priority become the PIM DR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure 'ip pim' on receiver interface on FRR1") + step("Enable PIM on all routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim drpriority 10' on receiver interface on FRR1(LHR)") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + raw_config = { + "l1": { + "raw_config": ["interface {}".format(intf_l1_r2), "ip pim drpriority 10"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "DR config is successful on FRR1 node , verify using " + " 'show ip pim interface json'" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_r2: {"drPriority": 10}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim drpriority 20' on receiver interface on FRR3(FHR)") + + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + raw_config = { + "f1": { + "raw_config": ["interface {}".format(intf_f1_r2), "ip pim drpriority 20"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "DR config is successful on FRR3 node , verify using " + " 'show ip pim interface json'" + ) + + input_dict_dr = {"f1": {"pim": {"interfaces": {intf_f1_r2: {"drPriority": 20}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "PIM is enable on FRR1, FRR2 interface and neighbor is up, " + " verify using 'show ip pim interface'" + ) + + result = verify_pim_interface(tgen, topo, "l1") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_pim_interface(tgen, topo, "f1") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Highet IP become PIM DR , verify using " + "'show ip pim interface json' and 'show ip pim neighbor'" + ) + step("Highest priority become PIM DR") + + dr_address = topo["routers"]["l1"]["links"]["r2"]["ipv4"].split("/")[0] + input_dict_dr = { + "l1": {"pim": {"interfaces": {intf_l1_r2: {"drAddress": dr_address}}}} + } + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dr_address = topo["routers"]["f1"]["links"]["r2"]["ipv4"].split("/")[0] + input_dict_dr = { + "f1": {"pim": {"interfaces": {intf_f1_r2: {"drAddress": dr_address}}}} + } + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim drpriority' on receiver interface on FRR1") + + raw_config = { + "l1": { + "raw_config": ["interface {}".format(intf_l1_r2), "no ip pim drpriority 10"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim drpriority' on receiver interface on FRR3") + + raw_config = { + "f1": { + "raw_config": ["interface {}".format(intf_f1_r2), "no ip pim drpriority 20"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "After removing drpriority , config got removed from both the " + "nodes and highest IP become PIM DR" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_r2: {"drPriority": 1}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + input_dict_dr = {"f1": {"pim": {"interfaces": {intf_f1_r2: {"drPriority": 1}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dr_address = topo["routers"]["r2"]["links"]["l1"]["ipv4"].split("/")[0] + input_dict_dr = { + "l1": {"pim": {"interfaces": {intf_l1_r2: {"drAddress": dr_address}}}} + } + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dr_address = topo["routers"]["r2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_dr = { + "f1": {"pim": {"interfaces": {intf_f1_r2: {"drAddress": dr_address}}}} + } + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_pim_hello_timer_p1(request): + """ + TC_37: Verify PIM hello is sent on configured timer + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure 'ip pim' on receiver interface on FRR1") + step("Enable PIM on all routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure PIM hello interval timer 100 on FRR1 node (FRR1-FRR2 link)") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_r2), "ip pim hello 100"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "PIM hello interval is configured on interface verify using " + "'show ip pim interface'" + ) + + input_dict_hello = { + "l1": {"pim": {"interfaces": {intf_l1_r2: {"helloPeriod": 100}}}} + } + result = verify_pim_config(tgen, input_dict_hello) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Modify hello timer to 180 and then 50sec") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_r2), "ip pim hello 180"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "PIM hello interval is configured on interface verify using " + "'show ip pim interface'" + ) + + input_dict_hello = { + "l1": {"pim": {"interfaces": {intf_l1_r2: {"helloPeriod": 180}}}} + } + result = verify_pim_config(tgen, input_dict_hello) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_r2), "ip pim hello 50"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "PIM hello interval is configured on interface verify using " + "'show ip pim interface'" + ) + + input_dict_hello = { + "l1": {"pim": {"interfaces": {intf_l1_r2: {"helloPeriod": 50}}}} + } + result = verify_pim_config(tgen, input_dict_hello) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_mroute_after_removing_RP_sending_IGMP_prune_p2(request): + """ + TC_39 Verify mroute after removing the RP and sending IGMP prune + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove cisco connected link to simulate topo " + "LHR(FRR1(f1))----RP(cisco(f1)---FHR(FRR3(l1))" + ) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_dict = { + "f1": {"igmp": {"interfaces": {intf_f1_i8: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i8": topo["routers"]["i8"]["links"]["f1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Send traffic from FHR to all the groups ( 225.1.1.1 to 225.1.1.5) and send" + " multicast traffic" + ) + + input_src = {"i6": topo["routers"]["i6"]["links"]["l1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + + input_dict_all = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove the RP config for both the range from all the nodes") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + "delete": True, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + input_dict_starg = [ + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroute still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send prune from receiver-1 (using ctrl+c) on iperf interface") + kill_iperf(tgen) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + traffic_before = verify_multicast_traffic( + tgen, input_traffic, return_traffic=True, expected=False + ) + assert isinstance(traffic_before, dict), ( + "Testcase {} : Failed \n traffic_before is not dictionary \n " + "Error: {}".format(tc_name, result) + ) + + step("IGMP groups are remove from FRR1 node 'show ip igmp groups'") + + dut = "f1" + result = verify_igmp_groups( + tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "IGMP groups still present still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step( + "After receiving the IGMP prune from FRR1 , verify traffic " + "immediately stopped for this receiver 'show ip multicast'" + ) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + traffic_after = verify_multicast_traffic( + tgen, input_traffic, return_traffic=True, expected=False + ) + assert isinstance(traffic_after, dict), ( + "Testcase {} : Failed \n traffic_after is not dictionary \n " + "Error: {}".format(tc_name, result) + ) + + result = verify_state_incremented(traffic_before, traffic_after) + assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result) + logger.info("Expected Behaviour: {}".format(result)) + + step("Configure static RP for (225.1.1.1-5) as R2 loopback interface") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send IGMP joins again from LHR,check IGMP joins and starg received") + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send traffic from FHR and verify mroute upstream") + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_prune_sent_to_LHR_and_FHR_when_PIMnbr_down_p2(request): + """ + TC_38 Verify prune is sent to LHR and FHR when PIM nbr went down + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove cisco connected link to simulate topo " + "LHR(FRR1(f1))----RP(cisco(f1)---FHR(FRR3(l1))" + ) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_dict = { + "f1": {"igmp": {"interfaces": {intf_f1_i8: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i8": topo["routers"]["i8"]["links"]["f1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Send traffic from FHR to all the groups ( 225.1.1.1 to 225.1.1.5) and send" + " multicast traffic" + ) + + input_src = { + "i6": topo["routers"]["i6"]["links"]["l1"]["interface"], + "i2": topo["routers"]["i2"]["links"]["f1"]["interface"], + } + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + source_i1 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + + input_dict_all = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i1, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + step("Verify mcast traffic received") + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the link from LHR to RP from RP node") + + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_f1, False) + + step("Verify RP info after Shut the link from LHR to RP from RP node") + dut = "f1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_starg = [ + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + } + ] + + input_dict_sg_i2 = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + input_dict_sg_i1 = [ + { + "dut": "f1", + "src_address": source_i1, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + } + ] + + input_dict_sg_i2_l1 = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + } + ] + + step("Verify mroute after Shut the link from LHR to RP from RP node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroute still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify upstream after Shut the link from LHR to RP from RP node") + + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "upstream still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("No shut the link from LHR to RP from RP node") + + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_f1, True) + + step("Verify RP info after No shut the link from LHR to RP from RP node") + dut = "f1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "RP iif is not updated \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("Verify mroute after No shut the link from LHR to RP from RP node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify upstrem after No shut the link from LHR to RP from RP node") + + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify mcast traffic received after noshut LHR to RP from RP node") + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the link from FHR to RP from RP node") + + intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_l1, False) + + kill_iperf(tgen, dut="i2", action="remove_traffic") + + step("Verify RP info after Shut the link from FHR to RP from RP node") + dut = "l1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroute after Shut the link from FHR to RP from RP node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify upstream after Shut the link from FHR to RP from RP node") + + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2_l1: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step(" No shut the link from FHR to RP from RP node") + + intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_l1, True) + + step("Verify RP info after Noshut the link from FHR to RP from RP node") + + dut = "l1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "RP iif is not updated \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("Verify mroute after Noshut the link from FHR to RP from RP node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify mroute after Noshut the link from FHR to RP from RP node") + + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify mcast traffic received after noshut FHR to RP from RP node") + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the link from FHR to RP from FHR node") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_r2, False) + + step("Verify PIM Nbrs after Shut the link from FHR to RP from FHR node") + + kill_iperf(tgen, dut="i6", action="remove_traffic") + + step("Verify RP info after Shut the link from FHR to RP from FHR node") + dut = "l1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroute after Shut the link from FHR to RP from FHR node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify upstream after Shut the link from FHR to RP from FHR node") + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2_l1: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step(" No shut the link from FHR to RP from FHR node") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_r2, True) + + step("Verify RP info after No Shut the link from FHR to RP from FHR node") + dut = "l1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "RP iif is not updated \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("Verify mroute after No Shut the link from FHR to RP from FHR node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify upstream after No Shut the link from FHR to RP from FHR node") + + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify mcast traffic received after noshut FHR to RP from FHR node") + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_mroute_flags_p1(request): + """ + TC_47 Verify mroute flag in LHR and FHR node + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove cisco connected link to simulate topo " + "LHR(FRR1(f1))----RP(cisco(f1)---FHR(FRR3(l1))" + ) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_dict = { + "f1": {"igmp": {"interfaces": {intf_f1_i8: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i8": topo["routers"]["i8"]["links"]["f1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Send traffic from FHR to all the groups ( 225.1.1.1 to 225.1.1.5) and send" + " multicast traffic" + ) + + input_src = { + "i6": topo["routers"]["i6"]["links"]["l1"]["interface"], + "i2": topo["routers"]["i2"]["links"]["f1"]["interface"], + } + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + source_i1 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + + input_dict_all = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i1, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dut = "f1" + step("verify flag for (*,G) on f1") + src_address = "*" + flag = "SC" + result = verify_multicast_flag_state( + tgen, dut, src_address, IGMP_JOIN_RANGE_1, flag + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify flag for (S,G) on f1 for Remote spurce ") + src_address = source_i2 + flag = "ST" + result = verify_multicast_flag_state( + tgen, dut, src_address, IGMP_JOIN_RANGE_1, flag + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_multicast_traffic_when_LHR_connected_to_RP_p1(request): + """ + TC_11: Verify multicast traffic flowing fine, when LHR connected to RP + Topology used: + FHR(FRR3(l1))---LHR(FRR1(r2)----RP(FRR2(f1)) + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Remove FRR3 to cisco connected link to simulate topo " + "FHR(FRR3(l1))---LHR(FRR1(r2)----RP(FRR2(f1))" + ) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + + step("Disable IGMP config from l1") + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": {"igmp": {"version": "2", "delete": True,}} + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers") + step( + "Enable IGMP on FRR1(r2) interface and send IGMP join (226.1.1.1-5)" + " and (232.1.1.1-5)" + ) + + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + input_dict = { + "r2": {"igmp": {"interfaces": {intf_r2_i3: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_join = {"i3": topo["routers"]["i3"]["links"]["r2"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in (f1)") + + input_dict = { + "f1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["f1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": _GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to 225.1.1.1-225.1.1.10" " receiver") + + input_src = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "'show ip mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + + source_i1 = topo["routers"]["i1"]["links"]["l1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": source_i1, + "iif": topo["routers"]["l1"]["links"]["i1"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i1, + "iif": topo["routers"]["r2"]["links"]["l1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Multicast traffic is flowing for all the groups verify" + "using 'show ip multicast'" + ) + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"] + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"] + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + input_traffic = { + "l1": {"traffic_received": [intf_l1_i1]}, + "r2": {"traffic_received": [intf_r2_l1], "traffic_sent": [intf_r2_i3]}, + } + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut and No shut the receiver port") + + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_i3, False) + + step( + "Verification: After Shut of receiver port, Verify (*,G) and " + "(S,G) got removed from LHR node (FRR1) using 'show ip mroute'" + ) + + input_dict_r2 = [ + { + "dut": "r2", + "src_address": "*", + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i1, + "iif": topo["routers"]["r2"]["links"]["l1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + ] + + for data in input_dict_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " Expected Behaviour: mroutes are cleared \n Error: {}".format( + tc_name, result + ) + ) + logger.info("Expected Behaviour: {}".format(result)) + + shutdown_bringup_interface(tgen, "r2", intf_r2_i3, True) + + step( + "Verification: After No shut of receiver port , Verify (*,G)" + " and (S,G) got populated on LHR node (FRR1) using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_r2: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Multicast traffic is resumed for all the groups verify " + "using 'show ip multicast'" + ) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut and No shut the source port") + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False) + + step( + "Verification: After Shut of source port, Verify (*,G) and " + "(S,G) got removed from LHR node (FRR1) using 'show ip mroute'" + ) + + input_dict_l1 = [ + { + "dut": "l1", + "src_address": source_i1, + "iif": topo["routers"]["l1"]["links"]["i1"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + } + ] + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + "mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True) + + step( + "Verification: After No shut of source port , Verify (*,G)" + " and (S,G) got populated on LHR node (FRR1) using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Multicast traffic is resumed for all the groups verify " + "using 'show ip multicast'" + ) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut and No shut of LHR to cisco port from LHR side") + + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_f1, False) + + step( + "Verification: After Shut of source port, Verify (S,G) got " + "removed from LHR and FHR using 'show ip mroute'" + ) + + input_dict_r2_f1 = [ + { + "dut": "r2", + "src_address": "*", + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_r2_f1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + shutdown_bringup_interface(tgen, "r2", intf_r2_f1, True) + + step( + "Verification: After No shut of source port , Verify (*,G)" + " and (S,G) got populated on LHR node (FRR1) using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Multicast traffic is resumed for all the groups verify " + "using 'show ip multicast'" + ) + + input_traffic_r2 = { + "r2": {"traffic_received": [intf_r2_l1], "traffic_sent": [intf_r2_i3]} + } + result = verify_multicast_traffic(tgen, input_traffic_r2) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut and no shut of FHR to LHR port from FHR side") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_r2, False) + + step( + "Verification: After Shut of LHR to FHR port, Verify (S,G)" + "got removed from LHR 'show ip mroute'" + ) + + dut = "r2" + src_address = "*" + iif = topo["routers"]["r2"]["links"]["f1"]["interface"] + oil = topo["routers"]["r2"]["links"]["i3"]["interface"] + + result = verify_ip_mroutes(tgen, dut, src_address, _IGMP_JOIN_RANGE, iif, oil) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + src_address = source_i1 + iif = topo["routers"]["r2"]["links"]["l1"]["interface"] + oil = topo["routers"]["r2"]["links"]["i3"]["interface"] + + result = verify_ip_mroutes( + tgen, dut, src_address, _IGMP_JOIN_RANGE, iif, oil, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + shutdown_bringup_interface(tgen, "l1", intf_l1_r2, True) + + step( + "Verification: After No shut of source port , Verify (*,G)" + " and (S,G) got populated on LHR node (FRR1) using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Multicast traffic is resumed for all the groups verify " + "using 'show ip multicast'" + ) + + result = verify_multicast_traffic(tgen, input_traffic_r2) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_multicast_traffic_when_FHR_connected_to_RP_p1(request): + """ + TC_12: Verify multicast traffic is flowing fine when FHR is connected to RP + Topology used: + LHR(FRR1)---FHR(FRR3)----RP(FRR2) + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Remove FRR3 to FRR2 connected link to simulate topo " + "FHR(FRR3)---LHR(FRR1)----RP(FFR2)" + ) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + + step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers") + step("Enable IGMP on FRR1(l1) interface and send IGMP join " " and (225.1.1.1-5)") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (225.1.1.1-5) in (f1)") + + input_dict = { + "f1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["f1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": _GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3(r2) to 225.1.1.1-225.1.1.10" " receiver") + + input_src = {"i3": topo["routers"]["i3"]["links"]["r2"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "'show ip mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + + source_i3 = topo["routers"]["i3"]["links"]["r2"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i3, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i3, + "iif": topo["routers"]["r2"]["links"]["i3"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + input_traffic = { + "l1": {"traffic_received": [intf_l1_r2], "traffic_sent": [intf_l1_i1]} + } + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the receiver(l1) port in 1 min interval") + + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False) + + step( + "Verification: After Shut of receiver port, Verify (*,G) and " + "(S,G) got removed from LHR node (FRR1) using 'show ip mroute'" + ) + + input_dict_l1 = [ + { + "dut": "l1", + "src_address": source_i3, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("No shut the receiver(l1) port in 1 min interval") + + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True) + + step( + "Verification: After No shut of receiver port , Verify (*,G)" + " and (S,G) got populated on LHR node (FRR1) using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the source(r2) port in 1 min interval") + + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_i3, False) + + step( + "Verification: After Shut of source port, Verify (S,G) got " + "removed from FHR using 'show ip mroute'" + ) + + input_dict_r2 = [ + { + "dut": "r2", + "src_address": source_i3, + "iif": topo["routers"]["r2"]["links"]["i3"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + } + ] + + for data in input_dict_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("No shut the source(r2) port in 1 min interval") + + shutdown_bringup_interface(tgen, "r2", intf_r2_i3, True) + + step( + "Verification: After No shut of source port , Verify (*,G)" + " and (S,G) got populated on LHR and FHR using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_r2: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut FHR to RP port from FHR side") + + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_f1, False) + + step( + "Verification: After Shut of FHR to cisco port, Verify (*,G) " + "got removed from FHR and cisco node using 'show ip mroute'" + ) + + input_dict_all_star = [ + { + "dut": "r2", + "src_address": "*", + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_all_star: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py new file mode 100755 index 0000000000..e8579e2a1e --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py @@ -0,0 +1,1122 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +""" +Following tests are covered to test multicast pim sm: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: + +1. TC:48 Verify mroute after configuring black-hole route for RP and source +2. TC:49 Verify mroute when RP is reachable using default route +3. TC:50 Verify mroute when LHR,FHR,RP and transit routers reachable + using default routes +4. TC:52 Verify PIM nbr after changing interface ip +5. TC:53 Verify IGMP interface updated with correct detail after changing interface config +6. TC:54 Verify received and transmit hello stats are getting cleared after PIM nbr reset + + +""" + +import os +import re +import sys +import json +import time +import datetime +from time import sleep +import pytest + +pytestmark = pytest.mark.pimd + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + iperfSendIGMPJoin, + addKernelRoute, + reset_config_on_routers, + iperfSendTraffic, + kill_iperf, + shutdown_bringup_interface, + start_router, + stop_router, + apply_raw_config, + create_static_routes, + required_linux_kernel_version, + topo_daemons, +) +from lib.pim import ( + create_pim_config, + create_igmp_config, + verify_igmp_groups, + verify_ip_mroutes, + clear_ip_pim_interface_traffic, + verify_igmp_config, + verify_pim_neighbors, + verify_pim_config, + verify_pim_interface, + verify_upstream_iif, + clear_ip_mroute, + verify_multicast_traffic, + verify_pim_rp_info, + verify_pim_interface_traffic, + verify_igmp_interface, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/multicast_pim_sm_topo4.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +TOPOLOGY = """ + + + i4-----c1-------------c2---i5 + | | + | | + i1-----l1------r2-----f1---i2 + | | | | + | | | | + i7 i6 i3 i8 + + Description: + i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP + join and traffic + l1 - LHR + f1 - FHR + r2 - FRR router + c1 - FRR router + c2 - FRR router +""" + +# Global variables + +GROUP_RANGE = "224.0.0.0/4" +IGMP_GROUP = "225.1.1.1/32" +IGMP_JOIN = "225.1.1.1" +GROUP_RANGE_1 = [ + "225.1.1.1/32", + "225.1.1.2/32", + "225.1.1.3/32", + "225.1.1.4/32", + "225.1.1.5/32", +] +IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"] +NEW_ADDRESS_1 = "192.168.20.1" +NEW_ADDRESS_2 = "192.168.20.2" +NEW_ADDRESS_1_SUBNET = "192.168.20.1/24" +NEW_ADDRESS_2_SUBNET = "192.168.20.2/24" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.19") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, iperf, iperf_intf, GROUP_RANGE, join=False, traffic=False +): + """ + API to do pre-configuration to send IGMP join and multicast + traffic + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `iperf`: router running iperf + * `iperf_intf`: interface name router running iperf + * `GROUP_RANGE`: group range + * `join`: IGMP join, default False + * `traffic`: multicast traffic, default False + """ + + if join: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + if traffic: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + router_list = tgen.routers() + for router in router_list.keys(): + if router == iperf: + continue + + rnode = router_list[router] + rnode.run("echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter") + + for router in topo["routers"].keys(): + if "static_routes" in topo["routers"][router]: + static_routes = topo["routers"][router]["static_routes"] + for static_route in static_routes: + network = static_route["network"] + next_hop = static_route["next_hop"] + if type(network) is not list: + network = [network] + + return True + + +def verify_state_incremented(state_before, state_after): + """ + API to compare interface traffic state incrementing + + Parameters + ---------- + * `state_before` : State dictionary for any particular instance + * `state_after` : State dictionary for any particular instance + """ + + for router, state_data in state_before.items(): + for state, value in state_data.items(): + if state_before[router][state] >= state_after[router][state]: + errormsg = ( + "[DUT: %s]: state %s value has not" + " incremented, Initial value: %s, " + "Current value: %s [FAILED!!]" + % ( + router, + state, + state_before[router][state], + state_after[router][state], + ) + ) + return errormsg + + logger.info( + "[DUT: %s]: State %s value is " + "incremented, Initial value: %s, Current value: %s" + " [PASSED!!]", + router, + state, + state_before[router][state], + state_after[router][state], + ) + + return True + + +def test_mroute_when_RP_reachable_default_route_p2(request): + """ + TC_49 Verify mroute when and source RP is reachable using default route + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove c1-c2 connected link to simulate topo " + "c1(FHR)---l1(RP)----r2---f1-----c2(LHR)" + ) + + intf_c1_c2 = topo["routers"]["c1"]["links"]["c2"]["interface"] + intf_c2_c1 = topo["routers"]["c2"]["links"]["c1"]["interface"] + shutdown_bringup_interface(tgen, "c1", intf_c1_c2, False) + shutdown_bringup_interface(tgen, "c2", intf_c2_c1, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_c2_i5 = topo["routers"]["c2"]["links"]["i5"]["interface"] + input_dict = { + "c2": {"igmp": {"interfaces": {intf_c2_i5: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i5": topo["routers"]["i5"]["links"]["c2"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "l1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["l1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send traffic from C1 to all the groups ( 225.1.1.1 to 225.1.1.5)") + + input_src = {"i4": topo["routers"]["i4"]["links"]["c1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i4 = topo["routers"]["i4"]["links"]["c1"]["ipv4"].split("/")[0] + + input_dict_starg = [ + { + "dut": "c2", + "src_address": "*", + "iif": topo["routers"]["c2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["c2"]["links"]["i5"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "c2", + "src_address": source_i4, + "iif": topo["routers"]["c2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["c2"]["links"]["i5"]["interface"], + } + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Delete static routes on c2") + input_dict = { + "c2": { + "static_routes": [ + { + "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"], + "next_hop": "10.0.3.2", + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify RP info unknown after removing static route from c2 ") + dut = "c2" + rp_address = topo["routers"]["l1"]["links"]["lo"]["ipv4"].split("/")[0] + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroute not present after Delete of static routes on c1") + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Configure default routes on c2") + + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["ipv4"].split("/")[0] + + input_dict = { + "c2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_f1_c2}]} + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("applying ip nht config on c2") + + raw_config = {"c2": {"raw_config": ["ip nht resolve-via-default"]}} + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify RP info is NOT unknown after removing static route from c2 ") + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify (s,g) populated after adding default route ") + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify (*,g) populated after adding default route ") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_mroute_with_RP_default_route_all_nodes_p2(request): + """ + TC_50 Verify mroute when LHR,FHR,RP and transit routers reachable + using default routes + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove c1-c2 connected link to simulate topo " + "c1(LHR)---l1(RP)----r2---f1-----c2(FHR)" + ) + + intf_c1_c2 = topo["routers"]["c1"]["links"]["c2"]["interface"] + intf_c2_c1 = topo["routers"]["c2"]["links"]["c1"]["interface"] + shutdown_bringup_interface(tgen, "c1", intf_c1_c2, False) + shutdown_bringup_interface(tgen, "c2", intf_c2_c1, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_c1_i4 = topo["routers"]["c1"]["links"]["i4"]["interface"] + input_dict = { + "c1": {"igmp": {"interfaces": {intf_c1_i4: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i4": topo["routers"]["i4"]["links"]["c1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "l1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["l1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send traffic from C2 to all the groups ( 225.1.1.1 to 225.1.1.5)") + + input_src = {"i5": topo["routers"]["i5"]["links"]["c2"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i5 = topo["routers"]["i5"]["links"]["c2"]["ipv4"].split("/")[0] + + input_dict_starg = [ + { + "dut": "c1", + "src_address": "*", + "iif": topo["routers"]["c1"]["links"]["l1"]["interface"], + "oil": topo["routers"]["c1"]["links"]["i4"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "c1", + "src_address": source_i5, + "iif": topo["routers"]["c1"]["links"]["l1"]["interface"], + "oil": topo["routers"]["c1"]["links"]["i4"]["interface"], + } + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Delete static routes RP on all the nodes") + input_dict = { + "c2": { + "static_routes": [ + {"network": ["1.0.4.11/32"], "next_hop": "10.0.3.2", "delete": True} + ] + }, + "c1": { + "static_routes": [ + {"network": ["1.0.4.11/32"], "next_hop": "10.0.2.2", "delete": True} + ] + }, + "r2": { + "static_routes": [ + {"network": ["1.0.4.11/32"], "next_hop": "10.0.12.1", "delete": True} + ] + }, + "f1": { + "static_routes": [ + {"network": ["1.0.4.11/32"], "next_hop": "10.0.7.2", "delete": True} + ] + }, + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify RP info unknown after removing static route from c2 ") + dut = "c2" + rp_address = topo["routers"]["l1"]["links"]["lo"]["ipv4"].split("/")[0] + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Configure default routes on all the nodes") + + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["ipv4"].split("/")[0] + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["ipv4"].split("/")[0] + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["ipv4"].split("/")[0] + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["ipv4"].split("/")[0] + + input_dict = { + "c1": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_l1_c1}]}, + "c2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_f1_c2}]}, + "r2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_l1_r2}]}, + "f1": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_r2_f1}]}, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("applying ip nht config on c2") + + raw_config = { + "c1": {"raw_config": ["ip nht resolve-via-default"]}, + "c2": {"raw_config": ["ip nht resolve-via-default"]}, + "r2": {"raw_config": ["ip nht resolve-via-default"]}, + "f1": {"raw_config": ["ip nht resolve-via-default"]}, + "l1": {"raw_config": ["ip nht resolve-via-default"]}, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify RP info Not unknown after removing static route from c2 ") + dut = "c2" + step("Verify RP info is NOT unknown after removing static route from c2 ") + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify (s,g) populated after adding default route ") + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify (*,g) populated after adding default route ") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_PIM_hello_tx_rx_p1(request): + """ + TC_54 Verify received and transmit hello stats + are getting cleared after PIM nbr reset + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove c1-c2 connected link to simulate topo " + "c1(LHR)---l1(RP)----r2---f1-----c2(FHR)" + ) + + intf_c1_c2 = topo["routers"]["c1"]["links"]["c2"]["interface"] + intf_c2_c1 = topo["routers"]["c2"]["links"]["c1"]["interface"] + shutdown_bringup_interface(tgen, "c1", intf_c1_c2, False) + shutdown_bringup_interface(tgen, "c2", intf_c2_c1, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_c1_i4 = topo["routers"]["c1"]["links"]["i4"]["interface"] + input_dict = { + "c1": {"igmp": {"interfaces": {intf_c1_i4: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i4": topo["routers"]["i4"]["links"]["c1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "l1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["l1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send Mcast traffic from C2 to all the groups ( 225.1.1.1 to 225.1.1.5)") + + input_src = {"i5": topo["routers"]["i5"]["links"]["c2"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i5 = topo["routers"]["i5"]["links"]["c2"]["ipv4"].split("/")[0] + + input_dict_starg = [ + { + "dut": "c1", + "src_address": "*", + "iif": topo["routers"]["c1"]["links"]["l1"]["interface"], + "oil": topo["routers"]["c1"]["links"]["i4"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "c1", + "src_address": source_i5, + "iif": topo["routers"]["c1"]["links"]["l1"]["interface"], + "oil": topo["routers"]["c1"]["links"]["i4"]["interface"], + } + ] + + step("(*,G) and (S,G) created on f1 and node verify using 'show ip mroute'") + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_c1_l1 = topo["routers"]["c1"]["links"]["l1"]["interface"] + + step("verify before stats on C1") + state_dict = {"c1": {intf_c1_l1: ["helloTx", "helloRx"],}} + + c1_state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + c1_state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("Flap PIM nbr while doing interface c1-l1 interface shut from f1 side") + shutdown_bringup_interface(tgen, "c1", intf_c1_l1, False) + + step( + "After shut of local interface from c1 , verify rx/tx hello counters are cleared on c1 side" + "verify using 'show ip pim interface traffic'" + ) + shutdown_bringup_interface(tgen, "c1", intf_c1_l1, True) + + step("verify stats after on c1") + c1_state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + c1_state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("verify stats not increamented on c1") + result = verify_state_incremented(c1_state_before, c1_state_after) + assert ( + result is not True + ), "Testcase{} : Failed Error: {}" "stats incremented".format(tc_name, result) + + step("verify before stats on l1") + l1_state_dict = {"l1": {intf_l1_c1: ["helloTx", "helloRx"],}} + + l1_state_before = verify_pim_interface_traffic(tgen, l1_state_dict) + assert isinstance( + l1_state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("Flap PIM nbr while doing interface r2-c1 shut from r2 side") + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + + step( + "After shut the interface from r2 side , verify r2 side rx and tx of hello" + "counters are resetted show ip pim interface traffic" + ) + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, True) + + step("verify stats after on l1") + l1_state_after = verify_pim_interface_traffic(tgen, l1_state_dict) + assert isinstance( + l1_state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("verify stats not increamented on l1") + result = verify_state_incremented(l1_state_before, l1_state_after) + assert ( + result is not True + ), "Testcase{} : Failed Error: {}" "stats incremented".format(tc_name, result) + + step("Reinit the dict") + c1_state_before = {} + l1_state_before = {} + c1_state_after = {} + l1_state_after = {} + + step("verify before stats on C1") + state_dict = {"c1": {intf_c1_l1: ["helloTx", "helloRx"],}} + + c1_state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + c1_state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("Flap c1-r2 pim nbr while changing ip address from c1 side") + c1_l1_ip_subnet = topo["routers"]["c1"]["links"]["l1"]["ipv4"] + + raw_config = { + "c1": { + "raw_config": [ + "interface {}".format(intf_c1_l1), + "no ip address {}".format(c1_l1_ip_subnet), + "ip address {}".format(NEW_ADDRESS_2_SUBNET), + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify stats after on c1") + c1_state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + c1_state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("verify stats not increamented on c1") + result = verify_state_incremented(c1_state_before, c1_state_after) + assert ( + result is not True + ), "Testcase{} : Failed Error: {}" "stats incremented".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast-pim-static-rp-topo1/__init__.py b/tests/topotests/multicast-pim-static-rp-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/multicast-pim-static-rp-topo1/__init__.py diff --git a/tests/topotests/multicast-pim-static-rp-topo1/multicast_pim_static_rp.json b/tests/topotests/multicast-pim-static-rp-topo1/multicast_pim_static_rp.json new file mode 100644 index 0000000000..6d6c047b00 --- /dev/null +++ b/tests/topotests/multicast-pim-static-rp-topo1/multicast_pim_static_rp.json @@ -0,0 +1,93 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "routers": { + "r0": {"links": {"r1": {"ipv4": "auto"}}}, + "r1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r0": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "r3": {"ipv4": "auto", "pim": "enable"}, + "r4": {"ipv4": "auto", "pim": "enable"} + }, + "pim": { + "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": ["224.0.0.0/4"]}] + }, + "igmp": {"interfaces": {"r1-r0-eth0": {"igmp": {"version": "2"}}}}, + "static_routes": [ + {"network": "10.0.4.0/24", "next_hop": "10.0.2.2"}, + {"network": "10.0.5.0/24", "next_hop": "10.0.2.2"}, + {"network": "10.0.6.0/24", "next_hop": "10.0.2.2", "admin_distance": 1}, + {"network": "10.0.6.0/24", "next_hop": "10.0.1.2", "admin_distance": 2}, + {"network": "1.0.2.17/32", "next_hop": "10.0.1.2", "admin_distance": 1}, + {"network": "1.0.2.17/32", "next_hop": "10.0.2.2", "admin_distance": 2}, + {"network": "1.0.3.17/32", "next_hop": "10.0.2.2"}, + {"network": "1.0.4.17/32", "next_hop": "10.0.3.2"} + ] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r1": {"ipv4": "auto", "pim": "enable"}, + "r3": {"ipv4": "auto", "pim": "enable"} + }, + "pim": { + "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": ["224.0.0.0/4"]}] + }, + "static_routes": [ + {"network": "10.0.0.0/24", "next_hop": "10.0.1.1"}, + {"network": "10.0.2.0/24", "next_hop": "10.0.1.1"}, + {"network": "10.0.3.0/24", "next_hop": "10.0.1.1"}, + {"network": "10.0.5.0/24", "next_hop": "10.0.4.2"}, + {"network": "10.0.6.0/24", "next_hop": "10.0.4.2"}, + {"network": "1.0.1.17/32", "next_hop": "10.0.1.1"}, + {"network": "1.0.3.17/32", "next_hop": "10.0.4.2"}, + {"network": "1.0.4.17/32", "next_hop": "10.0.1.1"} + ] + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r1": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "r4": {"ipv4": "auto", "pim": "enable"}, + "r5": {"ipv4": "auto", "pim": "enable"} + }, + "pim": { + "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": ["224.0.0.0/4"]}] + }, + "static_routes": [ + {"network": "10.0.0.0/24", "next_hop": "10.0.2.1"}, + {"network": "10.0.1.0/24", "next_hop": "10.0.2.1"}, + {"network": "10.0.3.0/24", "next_hop": "10.0.2.1"}, + {"network": "1.0.1.17/32", "next_hop": "10.0.2.1"}, + {"network": "1.0.2.17/32", "next_hop": "10.0.4.1"}, + {"network": "1.0.4.17/32", "next_hop": "10.0.5.2"} + ] + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r1": {"ipv4": "auto", "pim": "enable"}, + "r3": {"ipv4": "auto", "pim": "enable"} + }, + "pim": { + "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": ["224.0.0.0/4"]}] + }, + "static_routes": [ + {"network": "10.0.0.0/24", "next_hop": "10.0.3.1"}, + {"network": "10.0.1.0/24", "next_hop": "10.0.3.1"}, + {"network": "10.0.2.0/24", "next_hop": "10.0.3.1"}, + {"network": "10.0.4.0/24", "next_hop": "10.0.5.1"}, + {"network": "10.0.6.0/24", "next_hop": "10.0.5.1"}, + {"network": "1.0.1.17/32", "next_hop": "10.0.3.1"}, + {"network": "1.0.2.17/32", "next_hop": "10.0.3.1"}, + {"network": "1.0.3.17/32", "next_hop": "10.0.5.1"} + ] + }, + "r5": {"links": {"r3": {"ipv4": "auto"}}} + } +} diff --git a/tests/topotests/multicast-pim-static-rp-topo1/test_multicast_pim_static_rp.py b/tests/topotests/multicast-pim-static-rp-topo1/test_multicast_pim_static_rp.py new file mode 100755 index 0000000000..8dfdd50527 --- /dev/null +++ b/tests/topotests/multicast-pim-static-rp-topo1/test_multicast_pim_static_rp.py @@ -0,0 +1,3810 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +""" +Following tests are covered to test Multicast basic functionality: + +Topology: + + _______r2_____ + | | + iperf | | iperf + r0-----r1-------------r3-----r5 + | | + |_____________| + r4 + +Test steps +- Create topology (setup module) +- Bring up topology + +TC_1 : Verify upstream interfaces(IIF) and join state are updated properly + after adding and deleting the static RP +TC_2 : Verify IIF and OIL in "show ip pim state" updated properly after + adding and deleting the static RP +TC_3: (*, G) Mroute entry are cleared when static RP gets deleted +TC_4: Verify (*,G) prune is send towards the RP after deleting the static RP +TC_5: Verify OIF entry for RP is cleared when RP becomes unreachable +TC_6: Verify IIF and OIL in "show ip pim state" updated properly when RP + becomes unreachable +TC_7 : Verify upstream interfaces(IIF) and join state are updated properly + after adding and deleting the static RP +TC_8: Verify (*,G) prune is send towards the RP when RP becomes unreachable +TC_9 : Verify RP configured after IGMP join received, PIM join towards RP is + sent immediately +TC_10 : Verify RP becomes reachable after IGMP join received, PIM join + towards RP is sent immediately +TC_11 : Verify PIM join send towards the higher preferred RP +TC_12 : Verify PIM prune send towards the lower preferred RP +TC_13 : Verify RPF interface is updated in mroute (kernel) when higher + preferred overlapping RP configured +TC_14 : Verify IIF and OIL in "show ip pim state" updated properly when higher + preferred overlapping RP configured +TC_15 : Verify upstream interfaces(IIF) and join state are updated when higher + preferred overlapping RP is configured +TC_16 : Verify join is send to lower preferred RP, when higher preferred RP + gets deleted +TC_17 : Verify prune is send to higher preferred RP when higher preferred RP + gets deleted +TC_18 : Verify RPF interface updated in mroute when higher preferred RP gets + deleted +TC_19 : Verify IIF and OIL in "show ip pim state" updated when higher + preferred overlapping RP is deleted +TC_20 : Verfiy PIM upstream IIF updated when higher preferred overlapping RP + deleted +TC_21_1 : Verify OIF and RFP for (*,G) and (S,G) when static RP configure in + LHR router +TC_21_2 : Verify OIF and RFP for (*,G) and (S,G) when static RP configure in + LHR router +TC_22_1 : Verify OIF and RPF for (*,G) and (S,G) when static RP configure in + FHR router +TC_22_2 : Verify OIF and RPF for (*,G) and (S,G) when static RP configure in + FHR router +TC_23 : Verify (*,G) and (S,G) populated correctly when RPT and SPT path are + different +TC_24 : Verify (*,G) and (S,G) populated correctly when SPT and RPT share the + same path +TC_25 : Verify (*,G) and (S,G) populated correctly after clearing the PIM , + IGMP and mroutes joins +TC_26 : Restart the PIMd process and verify PIM joins , and mroutes entries +TC_27 : Configure multiple groups (10 grps) with same RP address +TC_28 : Configure multiple groups (10 grps) with different RP address +TC_29 : Verify IIF and OIL in updated in mroute when upstream interface + configure as RP +TC_30 : Verify IIF and OIL change to other path after shut the primary path +TC_31 : Verify RP info and (*,G) mroute after deleting the RP and shut / no + shut the RPF interface. +TC_32 : Verify RP info and (*,G) mroute after deleting the RP and shut / no + shut the RPF inteface +""" + +import os +import sys +import json +import time +import pytest +from time import sleep +import datetime + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + step, + iperfSendIGMPJoin, + iperfSendTraffic, + addKernelRoute, + shutdown_bringup_interface, + kill_router_daemons, + start_router_daemons, + create_static_routes, + kill_iperf, + topo_daemons, +) +from lib.pim import ( + create_pim_config, + verify_igmp_groups, + verify_upstream_iif, + verify_join_state_and_timer, + verify_ip_mroutes, + verify_pim_neighbors, + verify_pim_interface_traffic, + verify_pim_rp_info, + verify_pim_state, + clear_ip_pim_interface_traffic, + clear_ip_igmp_interfaces, + clear_ip_pim_interfaces, + clear_ip_mroute, + clear_ip_mroute_verify, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/multicast_pim_static_rp.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + logger.info("Could not read file:", jsonFile) + +# Global variables +GROUP_RANGE_ALL = "224.0.0.0/4" +GROUP_RANGE = "225.1.1.1/32" +GROUP_RANGE_LIST_1 = [ + "225.1.1.1/32", + "225.1.1.2/32", + "225.1.1.3/32", + "225.1.1.4/32", + "225.1.1.5/32", +] +GROUP_RANGE_LIST_2 = [ + "225.1.1.6/32", + "225.1.1.7/32", + "225.1.1.8/32", + "225.1.1.9/32", + "225.1.1.10/32", +] +GROUP_ADDRESS = "225.1.1.1" +GROUP_ADDRESS_LIST_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"] +GROUP_ADDRESS_LIST_2 = [ + "225.1.1.6", + "225.1.1.7", + "225.1.1.8", + "225.1.1.9", + "225.1.1.10", +] +STAR = "*" +SOURCE_ADDRESS = "10.0.6.2" +SOURCE = "Static" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + topology = """ + + _______r2_____ + | | + iperf | | iperf + r0-----r1-------------r3-----r5 + | | + |_____________| + r4 + + """ + logger.info("Master Topology: \n {}".format(topology)) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Verify PIM neighbors + result = verify_pim_neighbors(tgen, topo) + assert result is True, "setup_module :Failed \n Error:" " {}".format(result) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + +def config_to_send_igmp_join_and_traffic(tgen, tc_name): + """ + API to do pre-configuration to send IGMP join and multicast + traffic + + parameters: + ----------- + * `tgen`: topogen object + * `tc_name`: caller test case name + """ + + step("r0: Add route to kernal") + result = addKernelRoute(tgen, "r0", "r0-r1-eth0", GROUP_RANGE_ALL) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r5: Add route to kernal") + result = addKernelRoute(tgen, "r5", "r5-r3-eth0", GROUP_RANGE_ALL) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + rnode = tgen.routers()["r1"] + rnode.run("ip route add 10.0.6.0/24 via 10.0.2.2") + rnode = tgen.routers()["r2"] + rnode.run("ip route add 10.0.6.0/24 via 10.0.4.2") + rnode = tgen.routers()["r4"] + rnode.run("ip route add 10.0.6.0/24 via 10.0.5.1") + + router_list = tgen.routers() + for router in router_list.keys(): + rnode = router_list[router] + rnode.run("echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter") + + return True + + +def verify_mroute_repopulated(uptime_before, uptime_after): + """ + API to compare uptime for mroutes + + Parameters + ---------- + * `uptime_before` : Uptime dictionary for any particular instance + * `uptime_after` : Uptime dictionary for any particular instance + """ + + for group in uptime_before.keys(): + for source in uptime_before[group].keys(): + if set(uptime_before[group]) != set(uptime_after[group]): + errormsg = ( + "mroute (%s, %s) has not come" + " up after mroute clear [FAILED!!]" % (source, group) + ) + return errormsg + + d1 = datetime.datetime.strptime(uptime_before[group][source], "%H:%M:%S") + d2 = datetime.datetime.strptime(uptime_after[group][source], "%H:%M:%S") + if d2 >= d1: + errormsg = "mroute (%s, %s) is not " "repopulated [FAILED!!]" % ( + source, + group, + ) + return errormsg + + logger.info("mroute (%s, %s) is " "repopulated [PASSED!!]", source, group) + + return True + + +def verify_state_incremented(state_before, state_after): + """ + API to compare interface traffic state incrementing + + Parameters + ---------- + * `state_before` : State dictionary for any particular instance + * `state_after` : State dictionary for any particular instance + """ + + for router, state_data in state_before.items(): + for state, value in state_data.items(): + if state_before[router][state] >= state_after[router][state]: + errormsg = ( + "[DUT: %s]: state %s value has not" + " incremented, Initial value: %s, " + "Current value: %s [FAILED!!]" + % ( + router, + state, + state_before[router][state], + state_after[router][state], + ) + ) + return errormsg + + logger.info( + "[DUT: %s]: State %s value is " + "incremented, Initial value: %s, Current value: %s" + " [PASSED!!]", + router, + state, + state_before[router][state], + state_after[router][state], + ) + + return True + + +def test_add_delete_static_RP_p0(request): + """ + TC_1_P0 : Verify upstream interfaces(IIF) and join state are updated + properly after adding and deleting the static RP + TC_2_P0 : Verify IIF and OIL in "show ip pim state" updated properly + after adding and deleting the static RP + TC_3_P0: (*, G) Mroute entry are cleared when static RP gets deleted + TC_4_P0: Verify (*,G) prune is send towards the RP after deleting the + static RP + + Topology used: + r0------r1-----r2 + iperf DUT RP + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface and send IGMP " "join (225.1.1.1) to r1") + step("Configure r2 loopback interface as RP") + step("Enable PIM between r1 and r3") + + step("r1: Verify show ip igmp group without any IGMP join") + dut = "r1" + interface = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, interface, GROUP_ADDRESS, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: Verify show ip pim interface traffic without any IGMP join") + state_dict = {"r1": {"r1-r2-eth1": ["pruneTx"]}} + + state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("r0 : Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify RP info") + dut = "r1" + iif = "r1-r2-eth1" + rp_address = "1.0.2.17" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + step("r1: Verify ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify ip pim join") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + step("r1: Delete RP configuration") + + # Delete RP configuration + input_dict = { + "r1": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify RP info") + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: Verify upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: Verify upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: Verify PIM state") + result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: Verify ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: Verify show ip pim interface traffic without any IGMP join") + state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_SPT_RPT_path_same_p1(request): + """ + TC_24_P1 : Verify (*,G) and (S,G) populated correctly when SPT and RPT + share the same path + + Topology used: + ________r2_____ + | | + iperf | | iperf + r0-----r1 r3-----r5 + + r1 : LHR + r2 : RP + r3 : FHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + dut = "r1" + intf = "r1-r3-eth2" + shutdown_bringup_interface(tgen, dut, intf, False) + intf = "r1-r4-eth3" + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "r3" + intf = "r3-r1-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + intf = "r3-r4-eth2" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("Enable IGMP on r1 interface and send IGMP join (225.1.1.1) to R1") + step("Configure RP on r2 (loopback interface) for the group range" " 224.0.0.0/4") + step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers") + step("Send multicast traffic from R3") + + step("r2: Verify RP info") + dut = "r2" + rp_address = "1.0.2.17" + iif = "lo" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + dut = "r1" + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r5: Send multicast traffic for group 225.1.1.1") + result = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + dut = "r1" + iif = "r1-r2-eth1" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r2-eth1" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream IIF interface") + dut = "r2" + iif = "lo" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes") + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream IIF interface") + iif = "r2-r3-eth1" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r2-eth1" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_not_reachable_static_RP_p0(request): + """ + TC_5_P0: Verify OIF entry for RP is cleared when RP becomes unreachable + TC_6_P0: Verify IIF and OIL in "show ip pim state" updated properly when + RP becomes unreachable + TC_7_P0 : Verify upstream interfaces(IIF) and join state are updated + properly after adding and deleting the static RP + TC_8_P0: Verify (*,G) prune is send towards the RP when RP becomes + unreachable + + Topology used: + r0------r1-----r2 + iperf DUT RP + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + dut = "r1" + intf = "r1-r3-eth2" + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "r1" + intf = "r1-r4-eth3" + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + "r1: (*,G) prune is not sent towards the RP interface, verify using" + "show ip pim interface traffic" + ) + state_dict = {"r1": {"r1-r2-eth1": ["pruneTx"]}} + state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface and send IGMP " "join (225.1.1.1) to r1") + step("Configure r2 loopback interface as RP") + step("Enable PIM between r1 and r2") + + step("r0 : Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify rp info") + dut = "r1" + iif = "r1-r2-eth1" + rp_address = "1.0.2.17" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify PIM state") + result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 :Verify ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Make RP un-reachable") + dut = "r1" + input_dict = { + dut: { + "static_routes": [ + {"network": "1.0.2.17/32", "next_hop": "10.0.1.2", "delete": True} + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("r1: Check RP detail using show ip pim rp-info OIF should be unknown") + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "r1 : OIL should be same and IIF should be cleared on R1 verify" + "using show ip pim state" + ) + result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: upstream IIF should be unknown , verify using show ip pim" "upstream") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "r1: join state should not be joined and join timer should stop," + "verify using show ip pim upstream" + ) + result = verify_join_state_and_timer( + tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "r1: (*,G) prune is sent towards the RP interface, verify using" + "show ip pim interface traffic" + ) + state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + step("r1: (*, G) cleared from mroute table using show ip mroute") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Expected behavior: {}".format(result)) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_add_RP_after_join_received_p1(request): + """ + TC_9_P1 : Verify RP configured after IGMP join received, PIM join towards + RP is sent immediately + + Topology used: + r0------r1-----r2 + iperf DUT RP + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on R1 interface") + step("Configure r2 loopback interface as RP") + step("Enable PIM between r1 and r2") + step("Delete RP configuration from r1") + + step("r1: Delete RP configuration") + input_dict = { + "r1": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify rp-info") + dut = "r1" + rp_address = "1.0.2.17" + iif = "r1-r2-eth1" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("joinTx value before join sent") + state_dict = {"r1": {"r1-r2-eth1": ["joinTx"]}} + state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("r0 : Send IGMP join (225.1.1.1) to r1, when rp is not configured" "in r1") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: IGMP group is received on R1 verify using show ip igmp groups") + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: Verify upstream join state and join timer") + + result = verify_join_state_and_timer( + tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: Verify PIM state") + result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: Verify ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: Configure static RP") + input_dict = { + "r1": { + "pim": { + "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify rp-info") + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify PIM state") + result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + logger.info("Expected behavior: {}".format(result)) + + state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_reachable_static_RP_after_join_p0(request): + """ + TC_10_P0 : Verify RP becomes reachable after IGMP join received, PIM join + towards RP is sent immediately + + Topology used: + r0------r1-----r3 + iperf DUT RP + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface and send IGMP " "join (225.1.1.1) to r1") + step("Configure r2 loopback interface as RP") + step("Enable PIM between r1 and r2") + + step("r1 : Verify pim interface traffic") + state_dict = {"r1": {"r1-r2-eth1": ["joinTx"]}} + state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("r1: Make RP un-reachable") + dut = "r1" + intf = "r1-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, False) + intf = "r1-r3-eth2" + shutdown_bringup_interface(tgen, dut, intf, False) + intf = "r1-r4-eth3" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1: Verify rp-info") + rp_address = "1.0.2.17" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_ADDRESS, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Send IGMP join for 225.1.1.1") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify IGMP groups") + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify upstream IIF interface") + iif = "r1-r2-eth1" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1 : Verify upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1 : Verify PIM state") + result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1 : Verify ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r1: Make RP reachable") + intf = "r1-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, True) + intf = "r1-r3-eth2" + shutdown_bringup_interface(tgen, dut, intf, True) + intf = "r1-r4-eth3" + shutdown_bringup_interface(tgen, dut, intf, True) + + step("r1 : Verify rp-info") + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify PIM state") + result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + logger.info("Expected behavior: {}".format(result)) + + step("r1 : Verify pim interface traffic") + state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_send_join_on_higher_preffered_rp_p1(request): + """ + TC_11_P1 : Verify PIM join send towards the higher preferred RP + TC_12_P1 : Verify PIM prune send towards the lower preferred RP + TC_13_P1 : Verify RPF interface is updated in mroute (kernel) when higher + preferred overlapping RP configured + TC_14_P1 : Verify IIF and OIL in "show ip pim state" updated properly when + higher preferred overlapping RP configured + TC_15_P1 : Verify upstream interfaces(IIF) and join state are updated when + higher preferred overlapping RP is configured + TC_16_P1 : Verify join is send to lower preferred RP, when higher + preferred RP gets deleted + TC_17_P1 : Verify prune is send to higher preferred RP when higher + preferred RP gets deleted + TC_18_P1 : Verify RPF interface updated in mroute when higher preferred RP + gets deleted + TC_19_P1 : Verify IIF and OIL in "show ip pim state" updated when higher + preferred overlapping RP is deleted + TC_20_P1 : Verfiy PIM upstream IIF updated when higher preferred + overlapping RP deleted + + Topology used: + _______r2 + | + iperf | + r0-----r1 + | + |_______r4 + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface") + step("Configure RP on r2 (loopback interface) for the group range " "224.0.0.0/4") + step("Configure RP on r4 (loopback interface) for the group range " "225.1.1.1/32") + + step("r3 : Make all interface not reachable") + dut = "r3" + intf = "r3-r1-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + intf = "r3-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, False) + intf = "r3-r4-eth2" + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "r2" + intf = "r2-r3-eth1" + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "r4" + intf = "r4-r3-eth1" + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "r1" + intf = "r1-r3-eth1" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1 : Verify joinTx count before sending join") + state_dict = {"r1": {"r1-r4-eth3": ["joinTx"], "r1-r2-eth1": ["pruneTx"]}} + + state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("r0 : Send IGMP join for 225.1.1.1") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify IGMP groups") + dut = "r1" + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure static RP for group 225.1.1.1/32") + input_dict = { + "r4": { + "pim": { + "rp": [{"rp_addr": "1.0.4.17", "group_addr_range": ["225.1.1.1/32"],}] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify RP info for group 224.0.0.0/4") + rp_address_1 = "1.0.2.17" + iif = "r1-r2-eth1" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address_1, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify RP info for group 225.1.1.1") + rp_address_2 = "1.0.4.17" + iif = "r1-r4-eth3" + result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE, iif, rp_address_2, SOURCE) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify join is sent to higher preferred RP") + step("r1 : Verify prune is sent to lower preferred RP") + state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + step("r1 : Verify ip mroutes") + iif = "r1-r4-eth3" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify PIM state") + result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + clear_ip_pim_interface_traffic(tgen, topo) + + step("r1 : Verify joinTx, pruneTx count before RP gets deleted") + state_dict = {"r1": {"r1-r2-eth1": ["joinTx"], "r1-r4-eth3": ["pruneTx"]}} + + state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("r1 : Delete RP configuration for 225.1.1.1") + input_dict = { + "r1": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.4.17", + "group_addr_range": ["225.1.1.1/32"], + "delete": True, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify rp-info for group 224.0.0.0/4") + iif = "r1-r2-eth1" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address_1, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1 : Verify rp-info for group 225.1.1.1") + iif = "r1-r4-eth3" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE, oif, rp_address_2, SOURCE, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "r1 : Verify RPF interface updated in mroute when higher preferred" + "RP gets deleted" + ) + iif = "r1-r2-eth1" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + logger.info("Expected behavior: {}".format(result)) + + step( + "r1 : Verify IIF and OIL in show ip pim state updated when higher" + "preferred overlapping RP is deleted" + ) + result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "r1 : Verfiy upstream IIF updated when higher preferred overlapping" + "RP deleted" + ) + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "r1 : Verify upstream join state and join timer updated when higher" + "preferred overlapping RP deleted" + ) + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "r1 : Verify join is sent to lower preferred RP, when higher" + "preferred RP gets deleted" + ) + step( + "r1 : Verify prune is sent to higher preferred RP when higher" + " preferred RP gets deleted" + ) + state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_RP_configured_as_LHR_1_p1(request): + """ + TC_21_1_P1: Verify OIF and RPF for (*,G) and (S,G) when static RP configure + in LHR router + + Topology used: + ________r2_____ + | | + iperf | | iperf + r0-----r1-------------r3-----r5 + + r1 : LHR/RP + r3 : FHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface") + step("Configure RP on r1 (loopback interface) for the group range" " 224.0.0.0/4") + step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers") + step("Send the IGMP join from r0") + step("Send multicast traffic from r5") + + step("r1 , r2, r3, r4: Delete existing RP configuration" "configure r1(LHR) as RP") + input_dict = { + "r1": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r2": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r3": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r4": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Configure r1(LHR) as RP") + input_dict = { + "r1": { + "pim": { + "rp": [{"rp_addr": "1.0.1.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r2": { + "pim": { + "rp": [{"rp_addr": "1.0.1.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r3": { + "pim": { + "rp": [{"rp_addr": "1.0.1.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r4": { + "pim": { + "rp": [{"rp_addr": "1.0.1.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + shutdown_bringup_interface(tgen, "r1", "lo", False) + sleep(5) + shutdown_bringup_interface(tgen, "r1", "lo", True) + sleep(5) + + step("r1: Verify RP info") + dut = "r1" + rp_address = "1.0.1.17" + iif = "lo" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r5: Send multicast traffic for group 225.1.1.1") + result = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r3-eth2" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_RP_configured_as_LHR_2_p1(request): + """ + TC_21_2_P1: Verify OIF and RPF for (*,G) and (S,G) when static RP configure + in LHR router + + Topology used: + ________r2_____ + | | + iperf | | iperf + r0-----r1-------------r3-----r5 + + r1 : LHR/RP + r3 : FHR + + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface") + step("Configure RP on r1 (loopback interface) for the group range" " 224.0.0.0/4") + step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers") + step("Send multicast traffic from r5") + step("Send the IGMP join from r0") + + step("r1, r2, r3, r4: Delete existing RP configuration," "configure r1(LHR) as RP") + input_dict = { + "r1": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r2": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r3": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r4": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1, r2, r3, r4: Configure r1(LHR) as RP") + input_dict = { + "r1": { + "pim": { + "rp": [{"rp_addr": "1.0.1.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r2": { + "pim": { + "rp": [{"rp_addr": "1.0.1.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r3": { + "pim": { + "rp": [{"rp_addr": "1.0.1.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r4": { + "pim": { + "rp": [{"rp_addr": "1.0.1.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify RP info") + dut = "r1" + rp_address = "1.0.1.17" + iif = "lo" + result = verify_pim_rp_info(tgen, topo, dut, GROUP_ADDRESS, iif, rp_address, SOURCE) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r5: Send multicast traffic for group 225.1.1.1") + result = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r3-eth2" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_RP_configured_as_FHR_1_p1(request): + """ + TC_22_1_P1: Verify OIF and RFP for (*,G) and (S,G) when static RP configure + in FHR router + + Topology used: + ________r2_____ + | | + iperf | | iperf + r0-----r1-------------r3-----r5 + + r1 : LHR + r3 : FHR/RP + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface") + step("Configure RP on r2 (loopback interface) for the group range" " 225.1.1.0/24") + step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers") + step("Send the IGMP join from r0") + step("Send multicast traffic from r5") + + step("r1, r2, r3, r4: Delete existing RP configuration" "configure r3(FHR) as RP") + input_dict = { + "r1": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r2": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r3": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r4": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1, r2, r3, r4: Configure r3(FHR) as RP") + input_dict = { + "r1": { + "pim": { + "rp": [{"rp_addr": "1.0.3.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r2": { + "pim": { + "rp": [{"rp_addr": "1.0.3.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r3": { + "pim": { + "rp": [{"rp_addr": "1.0.3.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r4": { + "pim": { + "rp": [{"rp_addr": "1.0.3.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify RP info") + dut = "r1" + rp_address = "1.0.3.17" + iif = "r1-r3-eth2" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Verify IGMP groups") + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r5: Send multicast traffic for group 225.1.1.1") + result = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_RP_configured_as_FHR_2_p2(request): + """ + TC_22_2_P2: Verify OIF and RFP for (*,G) and (S,G) when static RP configure + in FHR router + + Topology used: + ________r2_____ + | | + iperf | | iperf + r0-----r1-------------r3-----r5 + + r1 : LHR + r3 : FHR/RP + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface") + step("Configure RP on r2 (loopback interface) for the group range" " 225.1.1.0/24") + step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers") + step("Send multicast traffic from r5") + step("Send the IGMP join from r0") + + step("r1, r2, r3, r4: Delete existing RP configuration" "configure r3(FHR) as RP") + input_dict = { + "r1": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r2": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r3": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + "r4": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1, r2, r3, r4: Configure r3(FHR) as RP") + input_dict = { + "r1": { + "pim": { + "rp": [{"rp_addr": "1.0.3.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r2": { + "pim": { + "rp": [{"rp_addr": "1.0.3.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r3": { + "pim": { + "rp": [{"rp_addr": "1.0.3.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + "r4": { + "pim": { + "rp": [{"rp_addr": "1.0.3.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify RP info") + dut = "r1" + rp_address = "1.0.3.17" + iif = "r1-r3-eth2" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r5: Send multicast traffic for group 225.1.1.1") + result = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Verify IGMP groups") + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r3-eth2" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_SPT_RPT_path_different_p1(request): + """ + TC_23_P1: Verify (*,G) and (S,G) populated correctly when RPT and SPT path + are different + + Topology used: + ________r2_____ + | | + iperf | | iperf + r0-----r1-------------r3-----r5 + + r1: LHR + r2: RP + r3: FHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface and send IGMP join (225.1.1.1) to r1") + step("Configure RP on r2 (loopback interface) for the group range" " 224.0.0.0/4") + step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers") + step("Send multicast traffic from r3") + + step("r2: Verify RP info") + dut = "r2" + rp_address = "1.0.2.17" + iif = "lo" + result = verify_pim_rp_info(tgen, topo, dut, GROUP_ADDRESS, iif, rp_address, SOURCE) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + dut = "r1" + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r5: Send multicast traffic for group 225.1.1.1") + result = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + iif = "r1-r2-eth1" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r3-eth2" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream IIF interface") + dut = "r2" + iif = "lo" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes") + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream IIF interface") + dut = "r2" + iif = "r2-r3-eth1" + result = verify_upstream_iif( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, joinState="NotJoined" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r2: Verify (S, G) ip mroutes") + oif = "none" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_clear_pim_configuration_p1(request): + """ + TC_25_P1: Verify (*,G) and (S,G) populated correctly after clearing the + PIM,IGMP and mroutes joins + + Topology used: + ________r2_____ + | | + iperf | | iperf + r0-----r1-------------r3-----r5 + | | + |_____________| + r4 + r1 : LHR + r2 : RP + r3 : FHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface") + step("Configure RP on r2 (loopback interface) for the group range" " 224.0.0.0/4") + step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers") + step("Send the IGMP join from r0") + step("Send multicast traffic from r5") + + step("r2: Verify RP info") + dut = "r2" + rp_address = "1.0.2.17" + oif = "lo" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, oif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + dut = "r1" + iif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, iif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r5: Send multicast traffic for group 225.1.1.1") + result = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + dut = "r1" + iif = "r1-r2-eth1" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups timer restarted") + result = clear_ip_igmp_interfaces(tgen, dut) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify PIM neighbor timer restarted") + result = clear_ip_pim_interfaces(tgen, dut) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify PIM mroute timer restarted") + result = clear_ip_mroute_verify(tgen, dut) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + write_test_footer(tc_name) + + +def test_restart_pimd_process_p2(request): + """ + TC_26_P2: Restart the PIMd process and verify PIM upstream and mroutes + entries + Topology used: + ________r2_____ + | | + iperf | | iperf + r0-----r1-------------r3-----r5 + | | + |_____________| + r4 + r1 : LHR + r2 : RP + r3 : FHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface and send IGMP join (225.1.1.1) to R1") + step("Configure RP on r3 (loopback interface) for the group range" " 224.0.0.0/4") + step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers") + step("Send multicast traffic from R3") + step("Restart the PIMd process") + + step("r2: Verify RP info") + dut = "r2" + rp_address = "1.0.2.17" + oif = "lo" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, oif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + dut = "r1" + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r5: Send multicast traffic for group 225.1.1.1") + result = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + iif = "r1-r2-eth1" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r3-eth2" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream IIF interface") + dut = "r2" + iif = "lo" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes") + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + dut = "r1" + iif = "r1-r2-eth1" + oil = "r1-r0-eth0" + logger.info("waiting for 10 sec to make sure old mroute time is higher") + sleep(10) + uptime_before = verify_ip_mroutes( + tgen, dut, STAR, GROUP_ADDRESS, iif, oil, return_uptime=True, wait=60 + ) + assert isinstance(uptime_before, dict), "Testcase{} : Failed Error: {}".format( + tc_name, result + ) + + step("r1: Kill pimd process") + kill_router_daemons(tgen, "r1", ["pimd"]) + + step("r1 : Start pimd process") + start_router_daemons(tgen, "r1", ["pimd"]) + + logger.info("Waiting for 5sec to get PIMd restarted and mroute" " re-learned..") + sleep(5) + + uptime_after = verify_ip_mroutes( + tgen, dut, STAR, GROUP_ADDRESS, iif, oil, return_uptime=True, wait=10 + ) + assert isinstance(uptime_after, dict), "Testcase{} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_mroute_repopulated(uptime_before, uptime_after) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_multiple_groups_same_RP_address_p2(request): + """ + TC_27_P2: Configure multiple groups (10 grps) with same RP address + + Topology used: + ________r2_____ + | | + iperf | | iperf + r0-----r1-------------r3-----r5 + + r1 : LHR + r2 : RP + r3 : FHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface and send IGMP join (225.1.1.1) to r1") + step("Configure RP on r2 (loopback interface) for the group range" "225.1.1.0/24") + step("Enable the PIM on all the interfaces of r1-r2-r3") + step("Send multicast traffic from r5 to all the groups") + step("r1 : Remove the groups to RP mapping one by one") + step("r1: Shut the upstream interfaces") + step("r1: No shut the upstream interfaces") + step("r1: Configure the RP again") + step("r1: Shut the receiver interfaces") + step("r1: No Shut the receiver interfaces") + step("r2: Verify RP info") + + step("r2: verify rp-info") + dut = "r2" + rp_address = "1.0.2.17" + oif = "lo" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, oif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + GROUP_ADDRESS_LIST = GROUP_ADDRESS_LIST_1 + GROUP_ADDRESS_LIST_2 + step("r0: Send IGMP join for 10 groups") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS_LIST, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + dut = "r1" + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r5: Send multicast traffic for group 225.1.1.1") + result = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS_LIST, 32, 2500) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + dut = "r1" + iif = "r1-r2-eth1" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r3-eth2" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream IIF interface") + dut = "r2" + iif = "lo" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes") + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream IIF interface") + dut = "r2" + iif = "r2-r3-eth1" + result = verify_upstream_iif( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, joinState="NotJoined" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r2: Verify (S, G) ip mroutes") + oif = "none" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Delete RP configuration") + input_dict = { + "r1": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Shut the interface r1-r2-eth1 from R1 to R2") + dut = "r1" + intf = "r1-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1: No Shut the interface r1-r2-eth1 from R1 to R2") + intf = "r1-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step("r1: Configure RP") + input_dict = { + "r1": { + "pim": { + "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": GROUP_RANGE_ALL,}] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Shut the interface r1-r0-eth0 from R1 to R2") + intf = "r1-r0-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1: No Shut the interface r1-r0-eth0 from R1 to R2") + intf = "r1-r0-eth0" + shutdown_bringup_interface(tgen, dut, intf, True) + + step("r1: Verify (*, G) upstream IIF interface") + dut = "r1" + iif = "r1-r2-eth1" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r3-eth2" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream IIF interface") + dut = "r2" + iif = "lo" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes") + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream IIF interface") + dut = "r2" + iif = "r2-r3-eth1" + result = verify_upstream_iif( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, joinState="NotJoined" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r2: Verify (S, G) ip mroutes") + oif = "none" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_multiple_groups_different_RP_address_p2(request): + """ + TC_28_P2: Verify IIF and OIL in updated in mroute when upstream interface + configure as RP + + Topology used: + ________r2_____ + | | + iperf | | iperf + r0-----r1-------------r3-----r5 + | | + |_____________| + r4 + r1 : LHR + r2 & r4 : RP + r3 : FHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Delete existing RP configuration") + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r2": { + "pim": { + "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": GROUP_RANGE_LIST_1,}] + } + }, + "r4": { + "pim": { + "rp": [{"rp_addr": "1.0.4.17", "group_addr_range": GROUP_RANGE_LIST_2,}] + } + }, + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify RP info") + dut = "r2" + rp_address = "1.0.2.17" + oif = "lo" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_LIST_1, oif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r4: Verify RP info") + dut = "r4" + rp_address = "1.0.4.17" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_LIST_2, oif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + GROUP_ADDRESS_LIST = GROUP_ADDRESS_LIST_1 + GROUP_ADDRESS_LIST_2 + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS_LIST, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + dut = "r1" + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS_LIST) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r5: Send multicast traffic for group 225.1.1.1") + result = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS_LIST, 32, 2500) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + dut = "r1" + iif = "r1-r2-eth1" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r3-eth2" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1 + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream IIF interface") + dut = "r2" + iif = "lo" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes") + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream IIF interface") + iif = "r2-r3-eth1" + result = verify_upstream_iif( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, joinState="NotJoined" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r2: Verify (S, G) ip mroutes") + oif = "none" + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + dut = "r1" + iif = "r1-r4-eth3" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r3-eth2" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2 + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r4: Verify (*, G) upstream IIF interface") + dut = "r4" + iif = "lo" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r4: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r4: Verify (*, G) ip mroutes") + oif = "r4-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r4: Verify (S, G) upstream IIF interface") + iif = "r4-r3-eth1" + result = verify_upstream_iif( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, joinState="NotJoined" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r4: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r4: Verify (S, G) ip mroutes") + oif = "none" + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Delete RP configuration") + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_LIST_1, + "delete": True, + } + ] + } + }, + "r4": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.4.17", + "group_addr_range": GROUP_RANGE_LIST_2, + "delete": True, + } + ] + } + }, + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1, r2, r3, r4: Re-configure RP") + input_dict = { + "r2": { + "pim": { + "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": GROUP_RANGE_LIST_1,}] + } + }, + "r4": { + "pim": { + "rp": [{"rp_addr": "1.0.4.17", "group_addr_range": GROUP_RANGE_LIST_2,}] + } + }, + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Shut the interface r1-r2-eth1 from R1 to R2") + dut = "r1" + intf = "r1-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1: No shut the interface r1-r2-eth1 from R1 to R2") + dut = "r1" + intf = "r1-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step("r1: Shut the interface r1-r2-eth1 from R1 to R4") + dut = "r1" + intf = "r1-r4-eth3" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1: No shut the interface r1-r2-eth1 from R1 to r4") + dut = "r1" + intf = "r1-r4-eth3" + shutdown_bringup_interface(tgen, dut, intf, True) + + step("r1: Shut the interface r1-r0-eth0 from R1 to R0") + dut = "r1" + intf = "r1-r0-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1: No Shut the interface r1-r0-eth0 from R1 to R0") + dut = "r1" + intf = "r1-r0-eth0" + shutdown_bringup_interface(tgen, dut, intf, True) + + step("r1: Verify (*, G) upstream IIF interface") + dut = "r1" + iif = "r1-r2-eth1" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r3-eth2" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1 + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream IIF interface") + dut = "r2" + iif = "lo" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes") + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream IIF interface") + iif = "r2-r3-eth1" + result = verify_upstream_iif( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, joinState="NotJoined" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r2: Verify (S, G) ip mroutes") + oif = "none" + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream IIF interface") + dut = "r1" + iif = "r1-r4-eth3" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream IIF interface") + iif = "r1-r3-eth2" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2 + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (S, G) ip mroutes") + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r4: Verify (*, G) upstream IIF interface") + dut = "r4" + iif = "lo" + result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r4: Verify (*, G) upstream join state and join timer") + result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r4: Verify (*, G) ip mroutes") + oif = "r4-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r4: Verify (S, G) upstream IIF interface") + iif = "r4-r3-eth1" + result = verify_upstream_iif( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, joinState="NotJoined" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r4: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r4: Verify (S, G) ip mroutes") + oif = "none" + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream IIF interface") + dut = "r3" + iif = "r3-r5-eth3" + result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (S, G) upstream join state and join timer") + result = verify_join_state_and_timer( + tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (S, G) ip mroutes") + oif = "r3-r1-eth0" + result = verify_ip_mroutes( + tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_shutdown_primary_path_p1(request): + """ + TC_30_P1: Verify IIF and OIL change to other path after shut the primary + path + + Topology used: + ________r2_____ + | | + iperf | | + r0-----r1-------------r3 + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + # Steps to execute + step("Enable IGMP on r1 interface") + step("Configure RP on r2 (loopback interface) for the group range" " 224.0.0.0/4") + step("r1: Shut the link from r1 to r2") + step("r3: Shut the link from r1 to r3") + step("r1: No shut the link from r1 to r2") + step("r3: No shut the link from r1 to r3") + + step("r1: Verify RP info") + dut = "r1" + rp_address = "1.0.2.17" + iif = "r1-r2-eth1" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes") + dut = "r2" + iif = "lo" + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Shut the interface r1-r2-eth1 from R1 to R2") + dut = "r1" + intf = "r1-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + "Verify after shut the R1 to R2 link , verify join is reaching to RP" + "via other path" + ) + + logger.info("Waiting for 110 sec only if test run with crucible") + + step("r1: Verify (*, G) ip mroutes") + dut = "r1" + iif = "r1-r3-eth2" + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes") + dut = "r2" + iif = "lo" + oif = "r2-r3-eth1" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (*, G) ip mroutes") + dut = "r3" + iif = "r3-r2-eth1" + oif = "r3-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Shut the link from R1 to R3 from R3 node") + dut = "r3" + intf = "r3-r1-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + "Verify after shut of R1 to R3 link , verify (*,G) entries got" + "cleared from all the node R1, R2, R3" + ) + + step("r1: Verify (*, G) ip mroutes") + dut = "r1" + iif = "r1-r3-eth2" + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r2: Verify (*, G) ip mroutes") + dut = "r2" + iif = "lo" + oif = "r2-r3-eth1" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: Verify (*, G) ip mroutes") + dut = "r3" + iif = "r3-r2-eth1" + oif = "r3-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r3: No shutdown the link from R1 to R3 from R3 node") + dut = "r3" + intf = "r3-r1-eth0" + shutdown_bringup_interface(tgen, dut, intf, True) + + step("r1: Verify (*, G) ip mroutes") + dut = "r1" + iif = "r1-r3-eth2" + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes") + dut = "r2" + iif = "lo" + oif = "r2-r3-eth1" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r3: Verify (*, G) ip mroutes") + dut = "r3" + iif = "r3-r2-eth1" + oif = "r3-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: No shutdown the link from R1 to R2 from R1 node") + dut = "r1" + intf = "r1-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step("r1: Verify (*, G) ip mroutes") + dut = "r1" + iif = "r1-r2-eth1" + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes") + dut = "r2" + iif = "lo" + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_delete_RP_shut_noshut_upstream_interface_p1(request): + """ + TC_31_P1: Verify RP info and (*,G) mroute after deleting the RP and shut / + no shut the RPF interface. + Topology used: + ________r2_____ + | | + iperf | | + r0-----r1-------------r3 + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface") + step("Configure RP on r2 (loopback interface) for the group range" " 224.0.0.0/4") + step("r1: Delete the RP config") + step("r1: Shut and no shut the upstream interface (R1-R2) connected link") + step("r1: Shut and no shut the OIL interface") + + step("r1: Verify RP info") + dut = "r1" + rp_address = "1.0.2.17" + iif = "r1-r2-eth1" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + dut = "r1" + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes created") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes created") + dut = "r2" + iif = "lo" + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Delete RP configuration") + + # Delete RP configuration + input_dict = { + "r1": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Shut the interface r1-r2-eth1 from R1 to R2") + dut = "r1" + intf = "r1-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1: No shutdown the interface r1-r2-eth1 from R1 to R2") + dut = "r1" + intf = "r1-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step("r1: Shutdown the OIL interface r1-r0-eth0 from R1 to R0 ") + dut = "r1" + intf = "r1-r0-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1: No shutdown the OIL interface r1-r0-eth0 from R1 to R0") + dut = "r1" + intf = "r1-r0-eth0" + shutdown_bringup_interface(tgen, dut, intf, True) + + step("r1: Verify (*, G) ip mroutes cleared") + dut = "r1" + iif = "r1-r2-eth1" + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r2: Verify (*, G) ip mroutes cleared") + dut = "r2" + iif = "lo" + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_delete_RP_shut_noshut_RP_interface_p1(request): + """ + TC_32_P1: Verify RP info and (*,G) mroute after deleting the RP and shut/ + no shut the RPF inteface + + Topology used: + ________r2_____ + | | + iperf | | + r0-----r1-------------r3 + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Creating configuration from JSON") + reset_config_on_routers(tgen) + kill_iperf(tgen) + clear_ip_mroute(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + step("pre-configuration to send IGMP join and multicast traffic") + result = config_to_send_igmp_join_and_traffic(tgen, tc_name) + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step("Enable IGMP on r1 interface") + step("Configure RP on r2 (lo) for the group range" " 224.0.0.0/4") + step("r2: Delete the RP configuration") + step("r2: Shut the RP interface (lo)") + step("r1: Shut the interface(r1-r2-eth1, r1-r3-eth2) towards rp") + + step("r1: Verify RP info") + dut = "r1" + rp_address = "1.0.2.17" + iif = "r1-r2-eth1" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r0: Send IGMP join") + result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify IGMP groups") + oif = "r1-r0-eth0" + result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r1: Verify (*, G) ip mroutes created") + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Verify (*, G) ip mroutes created") + dut = "r2" + iif = "lo" + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Delete RP configuration") + + # Delete RP configuration + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": "1.0.2.17", + "group_addr_range": GROUP_RANGE_ALL, + "delete": True, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("r2: Shut the RP interface lo") + dut = "r2" + intf = "lo" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1: Shut the interface r1-r2-eth1 towards RP") + dut = "r1" + intf = "r1-r2-eth1" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1: Shut the interface r1-r3-eth2 towards RP") + dut = "r1" + intf = "r1-r3-eth2" + shutdown_bringup_interface(tgen, dut, intf, False) + + step("r1: Verify (*, G) ip mroutes cleared") + dut = "r1" + iif = "r1-r2-eth1" + oif = "r1-r0-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("r2: Verify (*, G) ip mroutes cleared") + dut = "r2" + iif = "lo" + oif = "r2-r1-eth0" + result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.dot b/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.dot new file mode 100644 index 0000000000..2c6d0aab16 --- /dev/null +++ b/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.dot @@ -0,0 +1,107 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph ospf_topo1 { + label="ospf dual stack"; + + # Routers + r1 [ + label="r1\nrtr-id 1.1.1.1/32", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + r2 [ + label="r2\nrtr-id 2.2.2.2/32", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + r3 [ + label="r3\nrtr-id 3.3.3.3/32", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + r4 [ + label="r4\nrtr-id 4.4.4.4/32", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + r5 [ + label="r5\nrtr-id 5.5.5.5/32", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + + # Switches + s1 [ + label="s1\n10.0.13.0/24\n2013:13::/64", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + label="s2\n10.0.23.0/24\n2023:23::/64", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s3 [ + label="s3\n10.0.34.0/24\n2034:34::/64", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s4 [ + label="s4\n10.0.24.0/24\n2024:24::/64", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s5 [ + label="s5\n10.0.45.0/24\n2045:45::/64", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + subgraph cluster1 { + label="area 1.1.1.1" + + r1 -- s1 [label="eth0\n.1\n::1"]; + r3 -- s1 [label="eth0\n.3\n::3"]; + r3 -- s2 [label="eth1\n.3\n::3"]; + r2 -- s2 [label="eth0\n.2\n::2"]; + } + + subgraph cluster0 { + label="area 0.0.0.0" + + r3 -- s3 [label="eth2\n.3\n::3"]; + r4 -- s3 [label="eth0\n.4\n::4"]; + r2 -- s4 [label="eth1\n.2\n::2"]; + r4 -- s4 [label="eth1\n.4\n::4"]; + } + + subgraph cluster2 { + label="area 2.2.2.2" + + r4 -- s5 [label="eth2\n.4\n::4"]; + r5 -- s5 [label="eth0\n.5\n::5"]; + } +} diff --git a/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.jpg b/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.jpg Binary files differnew file mode 100644 index 0000000000..44efda8390 --- /dev/null +++ b/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.jpg diff --git a/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.json b/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.json new file mode 100644 index 0000000000..c8a3ce783b --- /dev/null +++ b/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.json @@ -0,0 +1,255 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r1": { + "links": { + "r3": { + "ipv4": "10.0.13.1/24", + "ipv6": "2013:13::1/64", + "ospf": { + "area": "1.1.1.1", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + }, + "ospf6": { + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "1.1.1.1", + "neighbors": { + "r3": {} + } + }, + "ospf6": { + "router_id": "1.1.1.1", + "neighbors": { + "r3": { + "area": "1.1.1.1" + } + } + } + }, + "r2": { + "links": { + "r3": { + "ipv4": "10.0.23.2/24", + "ipv6": "2023:23::2/64", + "ospf": { + "area": "1.1.1.1", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + }, + "ospf6": { + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r4": { + "ipv4": "10.0.24.2/24", + "ipv6": "2034:34::2/64", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + }, + "ospf6": { + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "2.2.2.2", + "neighbors": { + "r3": {}, + "r4": {} + } + }, + "ospf6": { + "router_id": "2.2.2.2", + "neighbors": { + "r3": { "area": "1.1.1.1" }, + "r4": { "area": "0.0.0.0" } + } + } + }, + "r3": { + "links": { + "r1": { + "ipv4": "10.0.13.3/24", + "ipv6": "2013:13::3/64", + "ospf": { + "area": "1.1.1.1", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + }, + "ospf6": { + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r2": { + "ipv4": "10.0.23.3/24", + "ipv6": "2023:23::3/64", + "ospf": { + "area": "1.1.1.1", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + }, + "ospf6": { + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r4": { + "ipv4": "10.0.34.3/24", + "ipv6": "2034:34::3/64", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + }, + "ospf6": { + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "3.3.3.3", + "neighbors": { + "r1": {}, + "r2": {}, + "r4": {} + } + }, + "ospf6": { + "router_id": "3.3.3.3", + "neighbors": { + "r1": { "area": "1.1.1.1" }, + "r2": { "area": "1.1.1.1" }, + "r4": { "area": "0.0.0.0" } + } + } + }, + "r4": { + "links": { + "r2": { + "ipv4": "10.0.24.4/24", + "ipv6": "2024:24::4/64", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + }, + "ospf6": { + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r3": { + "ipv4": "10.0.34.4/24", + "ipv6": "2034:34::4/64", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + }, + "ospf6": { + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r5": { + "ipv4": "10.0.45.4/24", + "ipv6": "2045:45::4/64", + "ospf": { + "area": "2.2.2.2", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + }, + "ospf6": { + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "4.4.4.4", + "neighbors": { + "r2": {}, + "r3": {}, + "r5": {} + } + }, + "ospf6": { + "router_id": "4.4.4.4", + "neighbors": { + "r2": { "area": "0.0.0.0" }, + "r3": { "area": "0.0.0.0" }, + "r5": { "area": "2.2.2.2" } + } + } + }, + "r5": { + "links": { + "r4": { + "ipv4": "10.0.45.5/24", + "ipv6": "2045:45::5/64", + "ospf": { + "area": "2.2.2.2", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + }, + "ospf6": { + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "5.5.5.5", + "neighbors": { + "r4": {} + } + }, + "ospf6": { + "router_id": "5.5.5.5", + "neighbors": { + "r4": { "area": "2.2.2.2" } + } + } + } + } +} diff --git a/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.py b/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.py new file mode 100644 index 0000000000..5e7802fa04 --- /dev/null +++ b/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.py @@ -0,0 +1,152 @@ +#!/usr/bin/python + +import os +import sys +import time +import pytest +import json + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + stop_router, + start_router, + verify_rib, + create_static_routes, + step, + start_router_daemons, + shutdown_bringup_interface, + topo_daemons, + create_prefix_lists, + create_interfaces_cfg, + run_frr_cmd, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.ospf import ( + verify_ospf_neighbor, + verify_ospf6_neighbor, + create_router_ospf, + create_router_ospf6, + verify_ospf_summary, + redistribute_ospf, + verify_ospf_database, +) + +# Global variables +topo = None + +# Reading the data from JSON File for topology creation +jsonFile = "{}/test_ospf_dual_stack.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + + +class CreateTopo(Topo): + """Test topology builder.""" + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """Sets up the pytest environment.""" + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen, daemons) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether OSPF converged + ospf_covergence_ipv4 = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence_ipv4 is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence_ipv4 + ) + + # Api call verify whether OSPF6 converged + ospf_covergence_ipv6 = verify_ospf6_neighbor(tgen, topo) + assert ospf_covergence_ipv6 is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence_ipv6 + ) + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + tgen = get_topogen() + # Stop topology and remove tmp files + tgen.stop_topology() + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +# +# ################################## +# Test cases start here. +# ################################## +# +# +def test_ospf_dual_stack(request): + """OSPF test dual stack.""" + + tc_name = request.node.name + write_test_header(tc_name) + + # Don't run this test if we have any failure. + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + + step("Bring up the base configuration as per the JSON topology") + reset_config_on_routers(tgen) + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf-tilfa-topo1/__init__.py b/tests/topotests/ospf-tilfa-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/__init__.py diff --git a/tests/topotests/ospf-tilfa-topo1/rt1/ospfd.conf b/tests/topotests/ospf-tilfa-topo1/rt1/ospfd.conf new file mode 100644 index 0000000000..eaef49225f --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt1/ospfd.conf @@ -0,0 +1,27 @@ +debug ospf sr +debug ospf ti-lfa +! +interface lo +! +interface eth-rt2 + ip ospf network point-to-point +! +interface eth-rt3 + ip ospf network point-to-point +! +router ospf + ospf router-id 1.1.1.1 + network 1.1.1.0/24 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + area 0.0.0.0 range 10.0.0.0/16 + area 0.0.0.0 range 1.1.1.0/24 + capability opaque + mpls-te on + mpls-te router-address 1.1.1.1 + router-info area 0.0.0.0 + passive-interface lo + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 10 +! diff --git a/tests/topotests/ospf-tilfa-topo1/rt1/step1/show_ip_route_initial.ref b/tests/topotests/ospf-tilfa-topo1/rt1/step1/show_ip_route_initial.ref new file mode 100644 index 0000000000..0ad2aaeade --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt1/step1/show_ip_route_initial.ref @@ -0,0 +1,156 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo" + } + ] + }, + { + "prefix":"1.1.1.1\/32", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo" + } + ] + } + ], + "1.1.1.2\/32":[ + { + "prefix":"1.1.1.2\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ] + } + ], + "1.1.1.3\/32":[ + { + "prefix":"1.1.1.3\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ], + "1.1.1.4\/32":[ + { + "prefix":"1.1.1.4\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ] + } + ], + "1.1.1.5\/32":[ + { + "prefix":"1.1.1.5\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt2" + } + ] + }, + { + "prefix":"10.0.1.0\/24", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt2" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt3" + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + }, + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ] +} diff --git a/tests/topotests/ospf-tilfa-topo1/rt1/step2/show_ip_route_initial.ref b/tests/topotests/ospf-tilfa-topo1/rt1/step2/show_ip_route_initial.ref new file mode 100644 index 0000000000..0ad2aaeade --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt1/step2/show_ip_route_initial.ref @@ -0,0 +1,156 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo" + } + ] + }, + { + "prefix":"1.1.1.1\/32", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo" + } + ] + } + ], + "1.1.1.2\/32":[ + { + "prefix":"1.1.1.2\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ] + } + ], + "1.1.1.3\/32":[ + { + "prefix":"1.1.1.3\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ], + "1.1.1.4\/32":[ + { + "prefix":"1.1.1.4\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ] + } + ], + "1.1.1.5\/32":[ + { + "prefix":"1.1.1.5\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt2" + } + ] + }, + { + "prefix":"10.0.1.0\/24", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt2" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt3" + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + }, + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ] +} diff --git a/tests/topotests/ospf-tilfa-topo1/rt1/step2/show_ip_route_link_protection.ref b/tests/topotests/ospf-tilfa-topo1/rt1/step2/show_ip_route_link_protection.ref new file mode 100644 index 0000000000..968570e193 --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt1/step2/show_ip_route_link_protection.ref @@ -0,0 +1,226 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo" + } + ] + }, + { + "prefix":"1.1.1.1\/32", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo" + } + ] + } + ], + "1.1.1.2\/32":[ + { + "prefix":"1.1.1.2\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ], + "backupNexthops":[ + { + "ip":"10.0.2.2", + "labels":[ + 16050 + ] + } + ] + } + ], + "1.1.1.3\/32":[ + { + "prefix":"1.1.1.3\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ], + "backupNexthops":[ + { + "ip":"10.0.1.2", + "labels":[ + 16040 + ] + } + ] + } + ], + "1.1.1.4\/32":[ + { + "prefix":"1.1.1.4\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2", + "labels":[ + 16040 + ] + } + ], + "backupNexthops":[ + { + "ip":"10.0.2.2", + "labels":[ + 16050, + 16040 + ] + } + ] + } + ], + "1.1.1.5\/32":[ + { + "prefix":"1.1.1.5\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3", + "labels":[ + 16050 + ] + } + ], + "backupNexthops":[ + { + "ip":"10.0.1.2", + "labels":[ + 16040, + 16050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt2" + } + ] + }, + { + "prefix":"10.0.1.0\/24", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt2" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt3" + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ], + "backupNexthops":[ + { + "ip":"10.0.2.2", + "labels":[ + 16050 + ] + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ], + "backupNexthops":[ + { + "ip":"10.0.1.2", + "labels":[ + 16040 + ] + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + }, + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ], + "backupNexthops":[ + { + "ip":"10.0.1.2", + "labels":[ + 16040 + ] + }, + { + "ip":"10.0.2.2", + "labels":[ + 16050 + ] + } + ] + } + ] +} diff --git a/tests/topotests/ospf-tilfa-topo1/rt1/step3/show_ip_route_initial.ref b/tests/topotests/ospf-tilfa-topo1/rt1/step3/show_ip_route_initial.ref new file mode 100644 index 0000000000..0ad2aaeade --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt1/step3/show_ip_route_initial.ref @@ -0,0 +1,156 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo" + } + ] + }, + { + "prefix":"1.1.1.1\/32", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo" + } + ] + } + ], + "1.1.1.2\/32":[ + { + "prefix":"1.1.1.2\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ] + } + ], + "1.1.1.3\/32":[ + { + "prefix":"1.1.1.3\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ], + "1.1.1.4\/32":[ + { + "prefix":"1.1.1.4\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ] + } + ], + "1.1.1.5\/32":[ + { + "prefix":"1.1.1.5\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt2" + } + ] + }, + { + "prefix":"10.0.1.0\/24", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt2" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt3" + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + }, + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ] +} diff --git a/tests/topotests/ospf-tilfa-topo1/rt1/step3/show_ip_route_node_protection.ref b/tests/topotests/ospf-tilfa-topo1/rt1/step3/show_ip_route_node_protection.ref new file mode 100644 index 0000000000..46a80d298e --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt1/step3/show_ip_route_node_protection.ref @@ -0,0 +1,192 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo" + } + ] + }, + { + "prefix":"1.1.1.1\/32", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo" + } + ] + } + ], + "1.1.1.2\/32":[ + { + "prefix":"1.1.1.2\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ] + } + ], + "1.1.1.3\/32":[ + { + "prefix":"1.1.1.3\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ], + "1.1.1.4\/32":[ + { + "prefix":"1.1.1.4\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2", + "labels":[ + 16040 + ] + } + ], + "backupNexthops":[ + { + "ip":"10.0.2.2", + "labels":[ + 16050 + ] + } + ] + } + ], + "1.1.1.5\/32":[ + { + "prefix":"1.1.1.5\/32", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3", + "labels":[ + 16050 + ] + } + ], + "backupNexthops":[ + { + "ip":"10.0.1.2", + "labels":[ + 16040 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt2" + } + ] + }, + { + "prefix":"10.0.1.0\/24", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt2" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt3" + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"ospf", + "nexthops":[ + { + "ip":"10.0.1.2", + "interfaceName":"eth-rt2" + }, + { + "ip":"10.0.2.2", + "interfaceName":"eth-rt3" + } + ], + "backupNexthops":[ + { + "ip":"10.0.2.2", + "labels":[ + 16050 + ] + }, + { + "ip":"10.0.1.2", + "labels":[ + 16040 + ] + } + ] + } + ] +} diff --git a/tests/topotests/ospf-tilfa-topo1/rt1/zebra.conf b/tests/topotests/ospf-tilfa-topo1/rt1/zebra.conf new file mode 100644 index 0000000000..bf0e77a17b --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt1/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname rt1 +! +interface lo + ip address 1.1.1.1/32 +! +interface eth-rt2 + ip address 10.0.1.1/24 +! +interface eth-rt3 + ip address 10.0.2.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf-tilfa-topo1/rt2/ospfd.conf b/tests/topotests/ospf-tilfa-topo1/rt2/ospfd.conf new file mode 100644 index 0000000000..7548aad7f8 --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt2/ospfd.conf @@ -0,0 +1,27 @@ +debug ospf sr +debug ospf ti-lfa +! +interface lo +! +interface eth-rt1 + ip ospf network point-to-point +! +interface eth-rt4 + ip ospf network point-to-point +! +router ospf + ospf router-id 1.1.1.2 + network 1.1.1.0/24 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + area 0.0.0.0 range 10.0.0.0/16 + area 0.0.0.0 range 1.1.1.0/24 + capability opaque + mpls-te on + mpls-te router-address 1.1.1.2 + router-info area 0.0.0.0 + passive-interface lo + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.2/32 index 20 +! diff --git a/tests/topotests/ospf-tilfa-topo1/rt2/zebra.conf b/tests/topotests/ospf-tilfa-topo1/rt2/zebra.conf new file mode 100644 index 0000000000..add2933571 --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt2/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname rt2 +! +interface lo + ip address 1.1.1.2/32 +! +interface eth-rt1 + ip address 10.0.1.2/24 +! +interface eth-rt4 + ip address 10.0.3.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf-tilfa-topo1/rt3/ospfd.conf b/tests/topotests/ospf-tilfa-topo1/rt3/ospfd.conf new file mode 100644 index 0000000000..6258295b6f --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt3/ospfd.conf @@ -0,0 +1,27 @@ +debug ospf sr +debug ospf ti-lfa +! +interface lo +! +interface eth-rt1 + ip ospf network point-to-point +! +interface eth-rt5 + ip ospf network point-to-point +! +router ospf + ospf router-id 1.1.1.3 + network 1.1.1.0/24 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + area 0.0.0.0 range 10.0.0.0/16 + area 0.0.0.0 range 1.1.1.0/24 + capability opaque + mpls-te on + mpls-te router-address 1.1.1.3 + router-info area 0.0.0.0 + passive-interface lo + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.3/32 index 30 +! diff --git a/tests/topotests/ospf-tilfa-topo1/rt3/zebra.conf b/tests/topotests/ospf-tilfa-topo1/rt3/zebra.conf new file mode 100644 index 0000000000..1bb64bc585 --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt3/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname rt3 +! +interface lo + ip address 1.1.1.3/32 +! +interface eth-rt1 + ip address 10.0.2.2/24 +! +interface eth-rt5 + ip address 10.0.4.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf-tilfa-topo1/rt4/ospfd.conf b/tests/topotests/ospf-tilfa-topo1/rt4/ospfd.conf new file mode 100644 index 0000000000..ad02214017 --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt4/ospfd.conf @@ -0,0 +1,27 @@ +debug ospf sr +debug ospf ti-lfa +! +interface lo +! +interface eth-rt2 + ip ospf network point-to-point +! +interface eth-rt5 + ip ospf network point-to-point +! +router ospf + ospf router-id 1.1.1.4 + network 1.1.1.0/24 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + area 0.0.0.0 range 10.0.0.0/16 + area 0.0.0.0 range 1.1.1.0/24 + capability opaque + mpls-te on + mpls-te router-address 1.1.1.4 + router-info area 0.0.0.0 + passive-interface lo + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.4/32 index 40 +! diff --git a/tests/topotests/ospf-tilfa-topo1/rt4/zebra.conf b/tests/topotests/ospf-tilfa-topo1/rt4/zebra.conf new file mode 100644 index 0000000000..306f0d4925 --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt4/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname rt4 +! +interface lo + ip address 1.1.1.4/32 +! +interface eth-rt2 + ip address 10.0.3.2/24 +! +interface eth-rt5 + ip address 10.0.5.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf-tilfa-topo1/rt5/ospfd.conf b/tests/topotests/ospf-tilfa-topo1/rt5/ospfd.conf new file mode 100644 index 0000000000..1b95858f53 --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt5/ospfd.conf @@ -0,0 +1,27 @@ +debug ospf sr +debug ospf ti-lfa +! +interface lo +! +interface eth-rt3 + ip ospf network point-to-point +! +interface eth-rt4 + ip ospf network point-to-point +! +router ospf + ospf router-id 1.1.1.5 + network 1.1.1.0/24 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + area 0.0.0.0 range 10.0.0.0/16 + area 0.0.0.0 range 1.1.1.0/24 + capability opaque + mpls-te on + mpls-te router-address 1.1.1.5 + router-info area 0.0.0.0 + passive-interface lo + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.5/32 index 50 +! diff --git a/tests/topotests/ospf-tilfa-topo1/rt5/zebra.conf b/tests/topotests/ospf-tilfa-topo1/rt5/zebra.conf new file mode 100644 index 0000000000..46f759580e --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/rt5/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname rt5 +! +interface lo + ip address 1.1.1.5/32 +! +interface eth-rt3 + ip address 10.0.4.2/24 +! +interface eth-rt4 + ip address 10.0.5.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf-tilfa-topo1/test_ospf_tilfa_topo1.py b/tests/topotests/ospf-tilfa-topo1/test_ospf_tilfa_topo1.py new file mode 100644 index 0000000000..eb3ad5d995 --- /dev/null +++ b/tests/topotests/ospf-tilfa-topo1/test_ospf_tilfa_topo1.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python + +# +# test_ospf_tilfa_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# 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_ospf_tilfa_topo1.py: + +This topology is intentionally kept simple, its main purpose is to verify that +generated backup label stacks are inserted correctly into the RIB. For fancy +topologies please use the unit test framework provided in `/tests/ospfd`. + + + +---------+ +---------+ + | | | | + 10.0.1.0/24 eth+rt1| RT2 |eth+rt4 eth+rt2| RT2 | + +---------------------+ 2.2.2.2 +---------------------+ 4.4.4.4 | + | | | 10.0.3.0/24 | | + |eth+rt2 +---------+ +---------+ + +---------+ eth+rt5| + | | | + | RT1 | 10.0.5.0/24| + | 1.1.1.1 | | + | | | + +---------+ eth+rt4| + |eth+rt3 +---------+ +---------+ + | | | 10.0.4.0/24 | | + +---------------------+ RT3 +---------------------+ RT5 | + 10.0.2.0/24 eth+rt1| 3.3.3.3 |eth+rt5 eth-rt3| 5.5.5.5 | + | | | | + +---------+ +---------+ +""" + +import os +import sys +import pytest +import json +import re +from time import sleep +from functools import partial + +# 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 + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["rt1", "rt2", "rt3", "rt4", "rt5"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1") + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1") + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2") + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3") + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4") + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def router_compare_json_output(rname, command, reference): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def test_ospf_initial_convergence_step1(): + logger.info("Test (step 1): check initial convergence") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_compare_json_output( + "rt1", + "show ip route json", + "step1/show_ip_route_initial.ref", + ) + +def test_ospf_link_protection_step2(): + logger.info("Test (step 2): check OSPF link protection") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # enable TI-LFA link protection on all interfaces + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "fast-reroute ti-lfa"' + ) + + router_compare_json_output( + "rt1", + "show ip route json", + "step2/show_ip_route_link_protection.ref", + ) + + # disable TI-LFA link protection on all interfaces + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "no fast-reroute ti-lfa"' + ) + + # check if we got back to the initial route table + router_compare_json_output( + "rt1", + "show ip route json", + "step2/show_ip_route_initial.ref", + ) + +def test_ospf_node_protection_step3(): + logger.info("Test (step 3): check OSPF node protection") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # enable TI-LFA node protection on all interfaces + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "fast-reroute ti-lfa node-protection"' + ) + + router_compare_json_output( + "rt1", + "show ip route json", + "step3/show_ip_route_node_protection.ref", + ) + + # disable TI-LFA node protection on all interfaces + tgen.net["rt1"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "no fast-reroute ti-lfa node-protection"' + ) + + # check if we got back to the initial route table + router_compare_json_output( + "rt1", + "show ip route json", + "step3/show_ip_route_initial.ref", + ) + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py b/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py index 6d44d02e5e..2421b312d2 100644 --- a/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py +++ b/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py @@ -320,8 +320,10 @@ def test_ospf_link_down_kernel_route(): # Run test function until we get an result. Wait at most 60 seconds. test_func = partial(compare_show_ip_route_vrf, router.name, expected) result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5) - assertmsg = 'OSPF IPv4 route mismatch in router "{}" after link down: {}'.format( - router.name, diff + assertmsg = ( + 'OSPF IPv4 route mismatch in router "{}" after link down: {}'.format( + router.name, diff + ) ) assert result, assertmsg diff --git a/tests/topotests/ospf-topo1/test_ospf_topo1.py b/tests/topotests/ospf-topo1/test_ospf_topo1.py index 95193afb2a..24806dd8fc 100644 --- a/tests/topotests/ospf-topo1/test_ospf_topo1.py +++ b/tests/topotests/ospf-topo1/test_ospf_topo1.py @@ -310,7 +310,10 @@ def test_ospf_json(): # r4 has more interfaces for area 0.0.0.1 if router.name == "r4": expected["areas"]["0.0.0.1"].update( - {"areaIfActiveCounter": 2, "areaIfTotalCounter": 2,} + { + "areaIfActiveCounter": 2, + "areaIfTotalCounter": 2, + } ) # router 3 has an additional area @@ -372,16 +375,25 @@ def test_ospf_link_down_kernel_route(): } if router.name == "r1" or router.name == "r2": expected.update( - {"10.0.10.0/24": None, "172.16.0.0/24": None, "172.16.1.0/24": None,} + { + "10.0.10.0/24": None, + "172.16.0.0/24": None, + "172.16.1.0/24": None, + } ) elif router.name == "r3" or router.name == "r4": expected.update( - {"10.0.1.0/24": None, "10.0.2.0/24": None,} + { + "10.0.1.0/24": None, + "10.0.2.0/24": None, + } ) # Route '10.0.3.0' is no longer available for r4 since it is down. if router.name == "r4": expected.update( - {"10.0.3.0/24": None,} + { + "10.0.3.0/24": None, + } ) assertmsg = 'OSPF IPv4 route mismatch in router "{}" after link down'.format( router.name @@ -443,12 +455,17 @@ def test_ospf6_link_down_kernel_route(): ) elif router.name == "r3" or router.name == "r4": expected.update( - {"2001:db8:1::/64": None, "2001:db8:2::/64": None,} + { + "2001:db8:1::/64": None, + "2001:db8:2::/64": None, + } ) # Route '2001:db8:3::/64' is no longer available for r4 since it is down. if router.name == "r4": expected.update( - {"2001:db8:3::/64": None,} + { + "2001:db8:3::/64": None, + } ) assertmsg = 'OSPF IPv6 route mismatch in router "{}" after link down'.format( router.name diff --git a/tests/topotests/ospf6-topo1/r2/ip_6_address.nhg.ref b/tests/topotests/ospf6-topo1/r2/ip_6_address.nhg.ref new file mode 100644 index 0000000000..032acb5341 --- /dev/null +++ b/tests/topotests/ospf6-topo1/r2/ip_6_address.nhg.ref @@ -0,0 +1,10 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fc00:2:2:2::1234 dev r2-stubnet proto XXXX metric 20 pref medium +fc00:2:2:2::/64 dev r2-stubnet proto XXXX metric 256 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r2-sw5 proto XXXX metric 256 pref medium +fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6-topo1/r3/ip_6_address.nhg.ref b/tests/topotests/ospf6-topo1/r3/ip_6_address.nhg.ref new file mode 100644 index 0000000000..101fcc95b4 --- /dev/null +++ b/tests/topotests/ospf6-topo1/r3/ip_6_address.nhg.ref @@ -0,0 +1,10 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fc00:3:3:3::1234 dev r3-stubnet proto XXXX metric 20 pref medium +fc00:3:3:3::/64 dev r3-stubnet proto XXXX metric 256 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r3-sw5 proto XXXX metric 256 pref medium +fc00:b:b:b::/64 dev r3-sw6 proto XXXX metric 256 pref medium diff --git a/tests/topotests/ospf6-topo1/r4/ip_6_address.nhg.ref b/tests/topotests/ospf6-topo1/r4/ip_6_address.nhg.ref new file mode 100644 index 0000000000..4f11670ce3 --- /dev/null +++ b/tests/topotests/ospf6-topo1/r4/ip_6_address.nhg.ref @@ -0,0 +1,10 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fc00:4:4:4::1234 dev r4-stubnet proto XXXX metric 20 pref medium +fc00:4:4:4::/64 dev r4-stubnet proto XXXX metric 256 pref medium +fc00:a:a:a::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 dev r4-sw6 proto XXXX metric 256 pref medium diff --git a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py index 8e3a329f10..c3efb6ff22 100644 --- a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py +++ b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py @@ -383,7 +383,15 @@ def test_linux_ipv6_kernel_routingTable(): "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s" % (i, diff) ) + else: + logger.error( + "r{} failed - no nhid ref file: {}".format(i, refTableFile) + ) + assert False, ( + "Linux Kernel IPv6 Routing Table verification failed for router r%s\n" + % (i) + ) def test_shutdown_check_stderr(): diff --git a/tests/topotests/ospf_basic_functionality/ospf_chaos.json b/tests/topotests/ospf_basic_functionality/ospf_chaos.json new file mode 100644 index 0000000000..ed199f181b --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_chaos.json @@ -0,0 +1,166 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + }, + "redistribute": [{ + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r1": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +} diff --git a/tests/topotests/ospf_basic_functionality/ospf_p2mp.json b/tests/topotests/ospf_basic_functionality/ospf_p2mp.json new file mode 100644 index 0000000000..40815f36f3 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_p2mp.json @@ -0,0 +1,198 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-multipoint" + } + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.0" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py index e92baefabf..9c3be58937 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py @@ -29,6 +29,7 @@ import pytest from time import sleep from copy import deepcopy import json +from lib.topotest import frr_unicode # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) @@ -318,7 +319,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): topo_modify_change_ip = deepcopy(topo) intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str( - IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3 ) + "/{}".format(intf_ip.split("/")[1]) build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) @@ -529,7 +530,7 @@ def test_ospf_authentication_md5_tc29_p1(request): intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str( - IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3 ) + "/{}".format(intf_ip.split("/")[1]) build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py new file mode 100644 index 0000000000..37b7528490 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py @@ -0,0 +1,576 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +from copy import deepcopy +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, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + step, + shutdown_bringup_interface, + topo_daemons, + verify_rib, + stop_router, start_router, + create_static_routes, + start_router_daemons, + kill_router_daemons +) + +from lib.ospf import ( + verify_ospf_neighbor, verify_ospf_rib, + create_router_ospf) + +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json +from ipaddress import IPv4Address + + + +# Global variables +topo = None + +NETWORK = { + "ipv4": ["11.0.20.1/32", "11.0.20.2/32", "11.0.20.3/32", "11.0.20.4/32", + "11.0.20.5/32"] +} +""" +Topology: + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. Verify ospf functionality after restart ospfd. +2. Verify ospf functionality after restart FRR service. +3. Verify ospf functionality when staticd is restarted. + """ + +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_chaos.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +# ################################## +# Test cases start here. +# ################################## +def test_ospf_chaos_tc31_p1(request): + """Verify ospf functionality after restart ospfd.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Create static routes(10.0.20.1/32) in R1 and redistribute " + "to OSPF using route map.") + + # Create Static routes + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK['ipv4'][0], + "no_of_ip": 5, + "next_hop": 'Null0', + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + ospf_red_r0 = { + "r0": { + "ospf": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify OSPF neighbors after base config is done.") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step("Verify that route is advertised to R1.") + dut = 'r1' + protocol = 'ospf' + nh = topo['routers']['r0']['links']['r1']['ipv4'].split('/')[0] + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Kill OSPFd daemon on R0.") + kill_router_daemons(tgen, "r0", ["ospfd"]) + + step("Verify OSPF neighbors are down after killing ospfd in R0") + dut = 'r0' + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, + expected=False) + assert ospf_covergence is not True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step("Verify that route advertised to R1 are deleted from RIB and FIB.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Bring up OSPFd daemon on R0.") + start_router_daemons(tgen, "r0", ["ospfd"]) + + step("Verify OSPF neighbors are up after bringing back ospfd in R0") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Kill OSPFd daemon on R1.") + kill_router_daemons(tgen, "r1", ["ospfd"]) + + step("Verify OSPF neighbors are down after killing ospfd in R1") + dut = 'r1' + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, + expected=False) + assert ospf_covergence is not True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step("Bring up OSPFd daemon on R1.") + start_router_daemons(tgen, "r1", ["ospfd"]) + + step("Verify OSPF neighbors are up after bringing back ospfd in R1") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_chaos_tc32_p1(request): + """Verify ospf functionality after restart FRR service. """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Create static routes(10.0.20.1/32) in R1 and redistribute " + "to OSPF using route map.") + + # Create Static routes + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK['ipv4'][0], + "no_of_ip": 5, + "next_hop": 'Null0', + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + ospf_red_r0 = { + "r0": { + "ospf": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify OSPF neighbors after base config is done.") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step("Verify that route is advertised to R1.") + dut = 'r1' + protocol = 'ospf' + + nh = topo['routers']['r0']['links']['r1']['ipv4'].split('/')[0] + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Restart frr on R0") + stop_router(tgen, 'r0') + start_router(tgen, 'r0') + + step("Verify OSPF neighbors are up after restarting R0") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Restart frr on R1") + stop_router(tgen, 'r1') + start_router(tgen, 'r1') + + step("Verify OSPF neighbors are up after restarting R1") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_chaos_tc34_p1(request): + """ + verify ospf functionality when staticd is restarted. + + Verify ospf functionalitywhen staticroutes are + redistributed & Staticd is restarted. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Create static routes(10.0.20.1/32) in R1 and redistribute " + "to OSPF using route map.") + + # Create Static routes + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK['ipv4'][0], + "no_of_ip": 5, + "next_hop": 'Null0', + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + ospf_red_r0 = { + "r0": { + "ospf": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify OSPF neighbors after base config is done.") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step("Verify that route is advertised to R1.") + dut = 'r1' + protocol = 'ospf' + nh = topo['routers']['r0']['links']['r1']['ipv4'].split('/')[0] + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Kill staticd daemon on R0.") + kill_router_daemons(tgen, "r0", ["staticd"]) + + step("Verify that route advertised to R1 are deleted from RIB and FIB.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Bring up staticd daemon on R0.") + start_router_daemons(tgen, "r0", ["staticd"]) + + step("Verify OSPF neighbors are up after bringing back ospfd in R0") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Kill staticd daemon on R1.") + kill_router_daemons(tgen, "r1", ["staticd"]) + + step("Bring up staticd daemon on R1.") + start_router_daemons(tgen, "r1", ["staticd"]) + + step("Verify OSPF neighbors are up after bringing back ospfd in R1") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py index 3b37b8a92f..441368e8fa 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py @@ -65,6 +65,7 @@ from lib.ospf import ( verify_ospf_rib, create_router_ospf, verify_ospf_interface, + redistribute_ospf, ) topo = None @@ -184,38 +185,6 @@ def teardown_module(mod): logger.info("=" * 40) -def red_static(dut, config=True): - """Local def for Redstribute static routes inside ospf.""" - global topo - tgen = get_topogen() - if config: - ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} - else: - ospf_red = { - dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}} - } - result = create_router_ospf(tgen, topo, ospf_red) - assert result is True, "Testcase : Failed \n Error: {}".format(result) - - -def red_connected(dut, config=True): - """Local def for Redstribute connected routes inside ospf.""" - global topo - tgen = get_topogen() - if config: - ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} - else: - ospf_red = { - dut: { - "ospf": { - "redistribute": [{"redist_type": "connected", "del_action": True}] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red) - assert result is True, "Testcase: Failed \n Error: {}".format(result) - - # ################################## # Test cases start here. # ################################## @@ -252,7 +221,11 @@ def test_ospf_ecmp_tc16_p0(request): input_dict = { "r0": { "static_routes": [ - {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",} + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + } ] } } @@ -260,7 +233,7 @@ def test_ospf_ecmp_tc16_p0(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) dut = "r0" - red_static(dut) + redistribute_ospf(tgen, topo, dut, "static") step("Verify that route in R2 in stalled with 8 next hops.") nh = [] @@ -341,7 +314,7 @@ def test_ospf_ecmp_tc16_p0(request): step(" Un configure static route on R0") dut = "r0" - red_static(dut, config=False) + redistribute_ospf(tgen, topo, dut, "static", delete=True) # Wait for R0 to flush external LSAs. sleep(10) @@ -372,7 +345,7 @@ def test_ospf_ecmp_tc16_p0(request): step("Re configure the static route in R0.") dut = "r0" - red_static(dut) + redistribute_ospf(tgen, topo, dut, "static") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) @@ -415,7 +388,11 @@ def test_ospf_ecmp_tc17_p0(request): input_dict = { "r0": { "static_routes": [ - {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",} + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + } ] } } @@ -423,7 +400,7 @@ def test_ospf_ecmp_tc17_p0(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) dut = "r0" - red_static(dut) + redistribute_ospf(tgen, topo, dut, "static") step("Verify that route in R2 in stalled with 2 next hops.") @@ -442,7 +419,7 @@ def test_ospf_ecmp_tc17_p0(request): step(" Un configure static route on R0") dut = "r0" - red_static(dut, config=False) + redistribute_ospf(tgen, topo, dut, "static", delete=True) # sleep till the route gets withdrawn sleep(10) @@ -472,7 +449,7 @@ def test_ospf_ecmp_tc17_p0(request): step("Reconfigure the static route in R0.Change ECMP value to 2.") dut = "r0" - red_static(dut) + redistribute_ospf(tgen, topo, dut, "static") step("Configure cost on R0 as 100") r0_ospf_cost = {"r0": {"links": {"r1": {"ospf": {"cost": 100}}}}} diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py index 967bc44879..2da1dcd21a 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py @@ -66,6 +66,7 @@ from lib.ospf import ( verify_ospf_rib, create_router_ospf, verify_ospf_interface, + redistribute_ospf, ) from ipaddress import IPv4Address @@ -187,42 +188,6 @@ def teardown_module(): pass -def red_static(dut, config=True): - """Local def for Redstribute static routes inside ospf.""" - global topo - tgen = get_topogen() - if config: - ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} - else: - ospf_red = { - dut: { - "ospf": { - "redistribute": [{"redist_type": "static", "del_action": True}] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red) - assert result is True, "Testcase : Failed \n Error: {}".format(result) - - -def red_connected(dut, config=True): - """Local def for Redstribute connected routes inside ospf.""" - global topo - tgen = get_topogen() - if config: - ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} - else: - ospf_red = { - dut: { - "ospf": { - "redistribute": [{"redist_type": "connected", "del_action": True}] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red) - assert result is True, "Testcase: Failed \n Error: {}".format(result) - - # ################################## # Test cases start here. # ################################## @@ -275,7 +240,7 @@ def test_ospf_lan_ecmp_tc18_p0(request): ) dut = rtr - red_static(dut) + redistribute_ospf(tgen, topo, dut, "static") step( "Verify that route in R0 in stalled with 8 hops. " diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py index 1357a86c81..dac32090bc 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py @@ -29,6 +29,7 @@ import pytest import json from copy import deepcopy import ipaddress +from lib.topotest import frr_unicode # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) @@ -182,42 +183,6 @@ def teardown_module(): pass -def red_static(dut, config=True): - """Local def for Redstribute static routes inside ospf.""" - global topo - tgen = get_topogen() - if config: - ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} - else: - ospf_red = { - dut: { - "ospf": { - "redistribute": [{"redist_type": "static", "del_action": True}] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red) - assert result is True, "Testcase : Failed \n Error: {}".format(result) - - -def red_connected(dut, config=True): - """Local def for Redstribute connected routes inside ospf.""" - global topo - tgen = get_topogen() - if config: - ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} - else: - ospf_red = { - dut: { - "ospf": { - "redistribute": [{"redist_type": "connected", "del_action": True}] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red) - assert result is True, "Testcase: Failed \n Error: {}".format(result) - - # ################################## # Test cases start here. # ################################## @@ -468,7 +433,7 @@ def test_ospf_lan_tc1_p0(request): topo_modify_change_ip = deepcopy(topo) intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str( - IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3 ) + "/{}".format(intf_ip.split("/")[1]) build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) @@ -601,7 +566,7 @@ def test_ospf_lan_tc2_p0(request): topo_modify_change_ip = deepcopy(topo) intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str( - IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3 ) + "/{}".format(intf_ip.split("/")[1]) build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) @@ -652,7 +617,7 @@ def test_ospf_lan_tc2_p0(request): topo_modify_change_ip = deepcopy(topo) intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str( - IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3 ) + "/{}".format(int(intf_ip.split("/")[1]) + 1) build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py index 82a34d046c..3644bff3dc 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py @@ -30,6 +30,7 @@ from lib.ospf import ( verify_ospf_rib, create_router_ospf, verify_ospf_interface, + redistribute_ospf, ) from lib.topojson import build_topo_from_json, build_config_from_json from lib.topolog import logger @@ -181,38 +182,6 @@ def teardown_module(mod): logger.info("=" * 40) -def red_static(dut, config=True): - """Local def for Redstribute static routes inside ospf.""" - global topo - tgen = get_topogen() - if config: - ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} - else: - ospf_red = { - dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}} - } - result = create_router_ospf(tgen, topo, ospf_red) - assert result is True, "Testcase : Failed \n Error: {}".format(result) - - -def red_connected(dut, config=True): - """Local def for Redstribute connected routes inside ospf.""" - global topo - tgen = get_topogen() - if config: - ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} - else: - ospf_red = { - dut: { - "ospf": { - "redistribute": [{"redist_type": "connected", "del_action": True}] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red) - assert result is True, "Testcase: Failed \n Error: {}".format(result) - - # ################################## # Test cases start here. # ################################## @@ -268,7 +237,7 @@ def test_ospf_learning_tc15_p0(request): step("Redistribute static route in R2 ospf.") dut = "r2" - red_static(dut) + redistribute_ospf(tgen, topo, dut, "static") step("Verify that Type 5 LSA is originated by R2.") dut = "r0" diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py new file mode 100644 index 0000000000..c90275ecb0 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py @@ -0,0 +1,416 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +import json +from copy import deepcopy +from ipaddress import IPv4Address + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen +import ipaddress + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + step, + create_route_maps, + shutdown_bringup_interface, + create_interfaces_cfg, + topo_daemons, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +from lib.ospf import ( + verify_ospf_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_interface, + verify_ospf_database, +) + +# Global variables +topo = None + +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_p2mp.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. OSPF P2MP -Verify state change events on p2mp network. + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_p2mp_tc1_p0(request): + """OSPF IFSM -Verify state change events on p2mp network.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + step( + "Verify that OSPF is subscribed to multi cast services " + "(All SPF, all DR Routers)." + ) + step("Verify that interface is enabled in ospf.") + step("Verify that config is successful.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "r3": {"ospf": {"mcastMemberOspfAllRouters": True, "ospfEnabled": True}} + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the ip address") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on the R0 interface") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "r3": { + "ospf": { + "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ + "r3" + ]["ipv4"].split("/")[0], + "ipAddressPrefixlen": int( + topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "ipv4" + ].split("/")[1] + ), + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Modify the mask on the R0 interface") + ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + mask = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + step("Delete the ip address") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": ip_addr, + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on the R0 interface") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(int(intf_ip.split("/")[1]) + 1) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "r3": { + "ospf": { + "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ + "r3" + ]["ipv4"].split("/")[0], + "ipAddressPrefixlen": int( + topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "ipv4" + ].split("/")[1] + ), + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "ipv4" + ], + "interface": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "interface" + ], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + build_config_from_json(tgen, topo, save_bkup=False) + + step("Change the area id on the interface") + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.0"}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.1"}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": {"links": {"r3": {"ospf": {"area": "0.0.0.1", "ospfEnabled": True}}}} + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.1"}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.0"}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify if interface is enabled with network type P2MP") + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": { + "area": "0.0.0.0", + "networkType":"POINTOMULTIPOINT" + }, + } + } + } + } + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py index 04b1f4b878..ceadb3975b 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py @@ -62,6 +62,7 @@ from lib.ospf import ( verify_ospf_rib, create_router_ospf, verify_ospf_database, + redistribute_ospf, ) # Global variables @@ -197,6 +198,7 @@ def teardown_module(mod): # Test cases start here. # ################################## + def test_ospf_routemaps_functionality_tc19_p0(request): """ OSPF Route map - Verify OSPF route map support functionality. @@ -215,121 +217,76 @@ def test_ospf_routemaps_functionality_tc19_p0(request): "r0": { "static_routes": [ { - "network": NETWORK['ipv4'][0], + "network": NETWORK["ipv4"][0], "no_of_ip": 5, - "next_hop": 'Null0', + "next_hop": "Null0", } ] } } result = create_static_routes(tgen, input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - ospf_red_r1 = { - "r0": { - "ospf": { - "redistribute": [{ - "redist_type": "static" - }] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red_r1) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) - - dut = 'r1' - lsid = NETWORK['ipv4'][0].split("/")[0] - rid = routerids[0] - protocol = 'ospf' + redistribute_ospf(tgen, topo, "r0", "static") + + dut = "r1" + lsid = NETWORK["ipv4"][0].split("/")[0] + rid = routerids[0] + protocol = "ospf" result = verify_ospf_rib(tgen, dut, input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - ospf_red_r1 = { - "r0": { - "ospf": { - "redistribute": [{ - "redist_type": "static", - "del_action": True - }] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red_r1) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + redistribute_ospf(tgen, topo, "r0", "static", delete=True) step( - 'Create prefix-list in R0 to permit 10.0.20.1/32 prefix &' - ' deny 10.0.20.2/32') + "Create prefix-list in R0 to permit 10.0.20.1/32 prefix &" " deny 10.0.20.2/32" + ) # Create ip prefix list pfx_list = { "r0": { "prefix_lists": { "ipv4": { - "pf_list_1_ipv4": [{ - "seqid": 10, - "network": NETWORK['ipv4'][0], - "action": "permit" - }, - { - "seqid": 11, - "network": "any", - "action": "deny" - } + "pf_list_1_ipv4": [ + { + "seqid": 10, + "network": NETWORK["ipv4"][0], + "action": "permit", + }, + {"seqid": 11, "network": "any", "action": "deny"}, ] } } } } result = create_prefix_lists(tgen, pfx_list) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) # Create route map routemaps = { - "r0": { - "route_maps": { - "rmap_ipv4": [{ + "r0": { + "route_maps": { + "rmap_ipv4": [ + { "action": "permit", - "match": { - "ipv4": { - "prefix_lists": - "pf_list_1_ipv4" - } - } - }] - } + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + } + ] } + } } result = create_route_maps(tgen, routemaps) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step( "Configure route map rmap1 and redistribute static routes to" - " ospf using route map rmap1") + " ospf using route map rmap1" + ) - ospf_red_r1 = { - "r0": { - "ospf": { - "redistribute": [{ - "redist_type": "static", - "route_map": "rmap_ipv4" - }] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red_r1) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4") step("Change prefix rules to permit 10.0.20.2 and deny 10.0.20.1") # Create ip prefix list @@ -337,65 +294,54 @@ def test_ospf_routemaps_functionality_tc19_p0(request): "r0": { "prefix_lists": { "ipv4": { - "pf_list_1_ipv4": [{ - "seqid": 10, - "network": NETWORK['ipv4'][1], - "action": "permit" - }, - { - "seqid": 11, - "network": "any", - "action": "deny" - } + "pf_list_1_ipv4": [ + { + "seqid": 10, + "network": NETWORK["ipv4"][1], + "action": "permit", + }, + {"seqid": 11, "network": "any", "action": "deny"}, ] } } } } result = create_prefix_lists(tgen, pfx_list) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("Verify that route 10.0.20.2 is allowed and 10.0.20.1 is denied.") - dut = 'r1' + dut = "r1" input_dict = { "r0": { "static_routes": [ - { - "network": NETWORK['ipv4'][1], - "no_of_ip": 1, - "next_hop": 'Null0' - } + {"network": NETWORK["ipv4"][1], "no_of_ip": 1, "next_hop": "Null0"} ] } } result = verify_ospf_rib(tgen, dut, input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) input_dict = { "r0": { "static_routes": [ - { - "network": NETWORK['ipv4'][0], - "no_of_ip": 1, - "next_hop": 'Null0' - } + {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": "Null0"} ] } } result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + tc_name, result + ) - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, - expected=False) + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) assert result is not True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + tc_name, result + ) step("Delete and reconfigure prefix list.") # Create ip prefix list @@ -403,114 +349,101 @@ def test_ospf_routemaps_functionality_tc19_p0(request): "r0": { "prefix_lists": { "ipv4": { - "pf_list_1_ipv4": [{ - "seqid": 10, - "network": NETWORK['ipv4'][1], - "action": "permit", - "delete": True - }, - { - "seqid": 11, - "network": "any", - "action": "deny", - "delete": True - } + "pf_list_1_ipv4": [ + { + "seqid": 10, + "network": NETWORK["ipv4"][1], + "action": "permit", + "delete": True, + }, + { + "seqid": 11, + "network": "any", + "action": "deny", + "delete": True, + }, ] } } } } result = create_prefix_lists(tgen, pfx_list) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) result = verify_prefix_lists(tgen, pfx_list) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) input_dict = { "r0": { "static_routes": [ - { - "network": NETWORK['ipv4'][0], - "no_of_ip": 5, - "next_hop": 'Null0' - } + {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0"} ] } } result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + tc_name, result + ) - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, - expected=False) + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) assert result is not True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + tc_name, result + ) pfx_list = { "r0": { "prefix_lists": { "ipv4": { - "pf_list_1_ipv4": [{ - "seqid": 10, - "network": NETWORK['ipv4'][1], - "action": "permit" - }, - { - "seqid": 11, - "network": "any", - "action": "deny" - } + "pf_list_1_ipv4": [ + { + "seqid": 10, + "network": NETWORK["ipv4"][1], + "action": "permit", + }, + {"seqid": 11, "network": "any", "action": "deny"}, ] } } } } result = create_prefix_lists(tgen, pfx_list) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("Verify that route 10.0.20.2 is allowed and 10.0.20.1 is denied.") - dut = 'r1' + dut = "r1" input_dict = { "r0": { "static_routes": [ - { - "network": NETWORK['ipv4'][1], - "no_of_ip": 1, - "next_hop": 'Null0' - } + {"network": NETWORK["ipv4"][1], "no_of_ip": 1, "next_hop": "Null0"} ] } } result = verify_ospf_rib(tgen, dut, input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) input_dict = { "r0": { "static_routes": [ - { - "network": NETWORK['ipv4'][0], - "no_of_ip": 1, - "next_hop": 'Null0' - } + {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": "Null0"} ] } } result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + tc_name, result + ) - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, - expected=False) + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) assert result is not True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + tc_name, result + ) write_test_footer(tc_name) @@ -535,7 +468,11 @@ def test_ospf_routemaps_functionality_tc20_p0(request): input_dict = { "r0": { "static_routes": [ - {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",} + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + } ] } } @@ -543,15 +480,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("Redistribute to ospf using route map ( non existent route map)") - ospf_red_r1 = { - "r0": { - "ospf": { - "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red_r1) - assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4") step( "Verify that routes are not allowed in OSPF even tough no " @@ -663,318 +592,221 @@ def test_ospf_routemaps_functionality_tc21_p0(request): step( "Create static routes(10.0.20.1/32) in R1 and redistribute " - "to OSPF using route map.") + "to OSPF using route map." + ) # Create Static routes input_dict = { "r0": { "static_routes": [ { - "network": NETWORK['ipv4'][0], + "network": NETWORK["ipv4"][0], "no_of_ip": 5, - "next_hop": 'Null0', + "next_hop": "Null0", } ] } } result = create_static_routes(tgen, input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - ospf_red_r0 = { - "r0": { - "ospf": { - "redistribute": [{ - "redist_type": "static", - "route_map": "rmap_ipv4" - }] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red_r0) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4") # Create route map routemaps = { - "r0": { - "route_maps": { - "rmap_ipv4": [{ - "action": "permit", - "seq_id": 10 - }] - } - } + "r0": {"route_maps": {"rmap_ipv4": [{"action": "permit", "seq_id": 10}]}} } result = create_route_maps(tgen, routemaps) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("Verify that route is advertised to R2.") - dut = 'r1' - protocol = 'ospf' + dut = "r1" + protocol = "ospf" result = verify_ospf_rib(tgen, dut, input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) # Create route map routemaps = { - "r0": { - "route_maps": { - "rmap_ipv4": [{ - "action": "permit", - "delete": True, - "seq_id": 10 - }] + "r0": { + "route_maps": { + "rmap_ipv4": [{"action": "permit", "delete": True, "seq_id": 10}] + } } } - } result = create_route_maps(tgen, routemaps) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step(' Configure route map with set clause (set metric)') + step(" Configure route map with set clause (set metric)") # Create route map routemaps = { - "r0": { - "route_maps": { - "rmap_ipv4": [{ - "action": "permit", - "set": { - "med": 123 - }, - "seq_id": 10 - }] + "r0": { + "route_maps": { + "rmap_ipv4": [{"action": "permit", "set": {"med": 123}, "seq_id": 10}] + } } } - } result = create_route_maps(tgen, routemaps) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("Verify that configured metric is applied to ospf routes.") - dut = 'r1' - protocol = 'ospf' + dut = "r1" + protocol = "ospf" result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step( "Configure route map with match clause (match metric) with " - "some actions(change metric).") + "some actions(change metric)." + ) # Create route map routemaps = { - "r0": { - "route_maps": { - "rmap_ipv4": [{ - "action": "permit", - "match": { - "med": 123 - }, - "set": { - "med": 150 - }, - "seq_id": 10 - }] + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"med": 123}, + "set": {"med": 150}, + "seq_id": 10, + } + ] + } } } - } result = create_route_maps(tgen, routemaps) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("Configure route map with call clause") # Create ip prefix list input_dict_2 = { - 'r0': { - 'prefix_lists': { - 'ipv4': { - 'pf_list_1_ipv4': [{ - 'seqid': 10, - 'network': 'any', - 'action': 'permit' - }] - } + "r0": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + } } } } result = create_prefix_lists(tgen, input_dict_2) - assert result is True, 'Testcase {} : Failed \n Error: {}'.format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) # Create route map input_dict_3 = { - "r0": { - "route_maps": { - "rmap_ipv4": [{ - "action": "permit", - "match": { - "ipv4": { - "prefix_lists": "pf_list_1_ipv4" - } - }, - "set": { - "med": 150 - }, - "call": "rmap_match_pf_2_ipv4", - "seq_id": 10 - }], - "rmap_match_pf_2_ipv4": [{ - "action": "permit", - "match": { - "ipv4": { - "prefix_lists": "pf_list_1_ipv4" - } - }, - "set": { - "med": 200 - }, - "seq_id": 10 - }] + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "set": {"med": 150}, + "call": "rmap_match_pf_2_ipv4", + "seq_id": 10, + } + ], + "rmap_match_pf_2_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "set": {"med": 200}, + "seq_id": 10, + } + ], + } } } - } result = create_route_maps(tgen, input_dict_3) - assert result is True, 'Testcase {} : Failed \n Error: {}'.format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) result = verify_ospf_rib(tgen, dut, input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) # Create route map - routemaps = { - "r0": { - "route_maps": { - "rmap_ipv4": [{ - "delete": True - }] - } - } - } + routemaps = {"r0": {"route_maps": {"rmap_ipv4": [{"delete": True}]}}} result = create_route_maps(tgen, routemaps) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("Configure route map with continue clause") # Create route map input_dict_3 = { - "r0": { - "route_maps": { - "rmap_ipv4": [{ - "action": "permit", - 'seq_id': '10', - "match": { - "ipv4": { - "prefix_lists": "pf_list_1_ipv4" - } - }, - "set": { - "med": 150 - }, - "continue": "30", - "seq_id": 10 - }, - { - "action": "permit", - "match": { - "ipv4": { - "prefix_lists": "pf_list_1_ipv4" - } - }, - "set": { - "med": 100 + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "set": {"med": 150}, + "continue": "30", + "seq_id": 10, }, - "seq_id": 20 - }, - { - "action": "permit", - "match": { - "ipv4": { - "prefix_lists": "pf_list_1_ipv4" - } + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "set": {"med": 100}, + "seq_id": 20, }, - "set": { - "med": 50 + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "set": {"med": 50}, + "seq_id": 30, }, - "seq_id": 30 - } - ] + ] + } } } - } result = create_route_maps(tgen, input_dict_3) - assert result is True, 'Testcase {} : Failed \n Error: {}'.format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) result = verify_ospf_rib(tgen, dut, input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("Configure route map with goto clause") # Create route map input_dict_3 = { - "r0": { - "route_maps": { - "rmap_ipv4": [{ - "action": "permit", - 'seq_id': '10', - "match": { - "ipv4": { - "prefix_lists": "pf_list_1_ipv4" - } + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "goto": "30", }, - "goto": "30", - }, - { - "action": "permit", - 'seq_id': '20', - "match": { - "ipv4": { - "prefix_lists": "pf_list_1_ipv4" - } + { + "action": "permit", + "seq_id": "20", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "set": {"med": 100}, }, - "set": { - "med": 100 - } - }, - { - "action": "permit", - 'seq_id': '30', - "match": { - "ipv4": { - "prefix_lists": "pf_list_1_ipv4" - } + { + "action": "permit", + "seq_id": "30", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "set": {"med": 200}, }, - "set": { - "med": 200 - } - } - ] + ] + } } } - } result = create_route_maps(tgen, input_dict_3) - assert result is True, 'Testcase {} : Failed \n Error: {}'.format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) write_test_footer(tc_name) @@ -1003,22 +835,18 @@ def test_ospf_routemaps_functionality_tc24_p0(request): input_dict = { "r0": { "static_routes": [ - {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": "Null0",} + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 1, + "next_hop": "Null0", + } ] } } result = create_static_routes(tgen, input_dict) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - ospf_red_r0 = { - "r0": { - "ospf": { - "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red_r0) - assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4") # Create ip prefix list pfx_list = { @@ -1037,9 +865,10 @@ def test_ospf_routemaps_functionality_tc24_p0(request): step("verify that prefix-list is created in R0.") result = verify_prefix_lists(tgen, pfx_list) - assert result is not True, ( - "Testcase {} : Failed \n Prefix list not " - "present. Error: {}".format(tc_name, result) + assert ( + result is not True + ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format( + tc_name, result ) # Create route map @@ -1105,9 +934,10 @@ def test_ospf_routemaps_functionality_tc24_p0(request): step("verify that prefix-list is created in R0.") result = verify_prefix_lists(tgen, pfx_list) - assert result is not True, ( - "Testcase {} : Failed \n Prefix list not " - "present. Error: {}".format(tc_name, result) + assert ( + result is not True + ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format( + tc_name, result ) # Create route map diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py index 6ac0b515df..5aa2779aee 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py @@ -61,6 +61,7 @@ from lib.ospf import ( clear_ospf, verify_ospf_rib, create_router_ospf, + redistribute_ospf, ) # Global variables @@ -183,42 +184,6 @@ def teardown_module(mod): logger.info("=" * 40) -def red_static(dut, config=True): - """Local def for Redstribute static routes inside ospf.""" - global topo - tgen = get_topogen() - if config: - ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} - else: - ospf_red = { - dut: { - "ospf": { - "redistribute": [{"redist_type": "static", "del_action": True}] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red) - assert result is True, "Testcase : Failed \n Error: {}".format(result) - - -def red_connected(dut, config=True): - """Local def for Redstribute connected routes inside ospf.""" - global topo - tgen = get_topogen() - if config: - ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} - else: - ospf_red = { - dut: { - "ospf": { - "redistribute": [{"redist_type": "connected", "del_action": True}] - } - } - } - result = create_router_ospf(tgen, topo, ospf_red) - assert result is True, "Testcase: Failed \n Error: {}".format(result) - - # ################################## # Test cases start here. # ################################## @@ -407,7 +372,13 @@ def test_ospf_redistribution_tc6_p0(request): protocol = "ospf" result = verify_rib( - tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh, expected=False, + tgen, + "ipv4", + dut, + input_dict, + protocol=protocol, + next_hop=nh, + expected=False, ) assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result @@ -480,8 +451,8 @@ def test_ospf_redistribution_tc8_p1(request): "advertised/exchaged via ospf" ) for rtr in topo["routers"]: - red_static(rtr) - red_connected(rtr) + redistribute_ospf(tgen, topo, rtr, "static") + redistribute_ospf(tgen, topo, rtr, "connected") for node in topo["routers"]: input_dict = { "r0": { @@ -538,18 +509,16 @@ def test_ospf_redistribution_tc8_p1(request): ) for rtr in topo["routers"]: - ospf_red = { - rtr: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}} - } - result = create_router_ospf(tgen, topo, ospf_red) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result - ) + redistribute_ospf(tgen, topo, rtr, "static", delete=True) input_dict = { "r0": { "static_routes": [ - {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",} + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + } ] } } diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py index 3a269d853c..6f6b119abc 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py @@ -29,6 +29,7 @@ import pytest import json from copy import deepcopy from ipaddress import IPv4Address +from lib.topotest import frr_unicode # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) @@ -233,7 +234,7 @@ def test_ospf_p2p_tc3_p0(request): topo_modify_change_ip = deepcopy(topo) intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str( - IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3 ) + "/{}".format(intf_ip.split("/")[1]) build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) @@ -284,7 +285,7 @@ def test_ospf_p2p_tc3_p0(request): topo_modify_change_ip = deepcopy(topo) intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str( - IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3 ) + "/{}".format(int(intf_ip.split("/")[1]) + 1) build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) @@ -776,7 +777,6 @@ def test_ospf_show_p1(request): write_test_footer(tc_name) - def test_ospf_dead_tc11_p0(request): """ OSPF timers. @@ -798,224 +798,146 @@ def test_ospf_dead_tc11_p0(request): step("modify dead interval from default value to some other value on r1") topo1 = { - 'r1': { - 'links': { - 'r0': { - 'interface': topo['routers']['r1']['links']['r0'][ - 'interface'], - 'ospf': { - 'hello_interval': 12, - 'dead_interval': 48 - } + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 12, "dead_interval": 48}, } } } } - result = create_interfaces_cfg(tgen, topo1) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step( "verify that new timer value is configured and applied using " - "the show ip ospf interface command.") - dut = 'r1' - input_dict= { - 'r1': { - 'links':{ - 'r0': { - 'ospf':{ - 'timerDeadSecs': 48 - } - } - } - } - } + "the show ip ospf interface command." + ) + dut = "r1" + input_dict = {"r1": {"links": {"r0": {"ospf": {"timerDeadSecs": 48}}}}} result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step( - "modify dead interval from default value to r1" - "dead interval timer on r2") + step("modify dead interval from default value to r1" "dead interval timer on r2") topo1 = { - 'r0': { - 'links': { - 'r1': { - 'interface': topo['routers']['r0']['links']['r1'][ - 'interface'], - 'ospf': { - 'dead_interval': 48, - 'hello_interval': 12 - } + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"dead_interval": 48, "hello_interval": 12}, } } } } result = create_interfaces_cfg(tgen, topo1) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) - + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("verify that new timer value is configured.") - input_dict= { - 'r0': { - 'links':{ - 'r1': { - 'ospf':{ - 'timerDeadSecs': 48 - } - } - } - } - } - dut = 'r0' + input_dict = {"r0": {"links": {"r1": {"ospf": {"timerDeadSecs": 48}}}}} + dut = "r0" result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("verify that ospf neighbours are full") ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, ("setup_module :Failed \n Error:" - " {}".format(ospf_covergence)) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) - step( - "reconfigure the default dead interval timer value to " - "default on r1 and r2") + step("reconfigure the default dead interval timer value to " "default on r1 and r2") topo1 = { - 'r0': { - 'links': { - 'r1': { - 'interface': topo['routers']['r0']['links']['r1'][ - 'interface'], - 'ospf': { - 'dead_interval': 40 - } + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"dead_interval": 40}, } } } } result = create_interfaces_cfg(tgen, topo1) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) topo1 = { - 'r1': { - 'links': { - 'r0': { - 'interface': topo['routers']['r1']['links']['r0'][ - 'interface'], - 'ospf': { - 'dead_interval': 40 - } + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"dead_interval": 40}, } } } } result = create_interfaces_cfg(tgen, topo1) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("verify that new timer value is configured.") - input_dict= { - 'r0': { - 'links':{ - 'r1': { - 'ospf':{ - 'timerDeadSecs': 40 - } - } - } - } - } - dut = 'r0' + input_dict = {"r0": {"links": {"r1": {"ospf": {"timerDeadSecs": 40}}}}} + dut = "r0" result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) - + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("verify that ospf neighbours are full") ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, ("setup_module :Failed \n Error:" - " {}".format(ospf_covergence)) - + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) step(" Configure dead timer = 65535 on r1 and r2") topo1 = { - 'r0': { - 'links': { - 'r1': { - 'interface': topo['routers']['r0']['links']['r1'][ - 'interface'], - 'ospf': { - 'dead_interval': 65535 - } + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"dead_interval": 65535}, } } } } result = create_interfaces_cfg(tgen, topo1) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) topo1 = { - 'r1': { - 'links': { - 'r0': { - 'interface': topo['routers']['r1']['links']['r0'][ - 'interface'], - 'ospf': { - 'dead_interval': 65535 - } + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"dead_interval": 65535}, } } } } result = create_interfaces_cfg(tgen, topo1) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("verify that new timer value is configured.") - input_dict= { - 'r0': { - 'links':{ - 'r1': { - 'ospf':{ - 'timerDeadSecs': 65535 - } - } - } - } - } - dut = 'r0' + input_dict = {"r0": {"links": {"r1": {"ospf": {"timerDeadSecs": 65535}}}}} + dut = "r0" result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("verify that ospf neighbours are full") ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, ("setup_module :Failed \n Error:" - " {}".format(ospf_covergence)) - + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) step(" Try configuring timer values outside range for example 65536") topo1 = { - 'r0': { - 'links': { - 'r1': { - 'interface': topo['routers']['r0']['links']['r1'][ - 'interface'], - 'ospf': { - 'dead_interval': 65536 - } + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"dead_interval": 65536}, } } } @@ -1023,47 +945,33 @@ def test_ospf_dead_tc11_p0(request): result = create_interfaces_cfg(tgen, topo1) assert result is not True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + tc_name, result + ) step("Unconfigure the dead timer from the interface from r1 and r2.") topo1 = { - 'r1': { - 'links': { - 'r0': { - 'interface': topo['routers']['r1']['links']['r0'][ - 'interface'], - 'ospf': { - 'dead_interval': 65535 - }, - 'delete': True + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"dead_interval": 65535}, + "delete": True, } } } } result = create_interfaces_cfg(tgen, topo1) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step( - "Verify that timer value is deleted from intf & " - "set to default value 40 sec.") - input_dict= { - 'r1': { - 'links':{ - 'r0': { - 'ospf':{ - 'timerDeadSecs': 40 - } - } - } - } - } - dut = 'r1' + "Verify that timer value is deleted from intf & " "set to default value 40 sec." + ) + input_dict = {"r1": {"links": {"r0": {"ospf": {"timerDeadSecs": 40}}}}} + dut = "r1" result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) write_test_footer(tc_name) diff --git a/tests/topotests/pbr-topo1/test_pbr_topo1.py b/tests/topotests/pbr-topo1/test_pbr_topo1.py index fcbe3c0adf..5161d5eec7 100644 --- a/tests/topotests/pbr-topo1/test_pbr_topo1.py +++ b/tests/topotests/pbr-topo1/test_pbr_topo1.py @@ -80,7 +80,7 @@ class NetworkTopo(Topo): ## ##################################################### - +@pytest.mark.pbr def setup_module(module): "Setup topology" tgen = Topogen(NetworkTopo, module.__name__) diff --git a/tests/topotests/pim-basic/test_pim.py b/tests/topotests/pim-basic/test_pim.py index e8a9f72b48..224b82f1fb 100644 --- a/tests/topotests/pim-basic/test_pim.py +++ b/tests/topotests/pim-basic/test_pim.py @@ -31,6 +31,8 @@ import pytest import json from functools import partial +pytestmark = pytest.mark.pimd + CWD = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(CWD, "../")) diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index 6e8e749092..0c45a09445 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -2,6 +2,29 @@ [pytest] norecursedirs = .git example-test example-topojson-test lib docker +# Markers +# +# Please consult the documentation and discuss with TSC members before applying +# any changes to this list. +markers = + babeld: Tests that run against BABELD + bfdd: Tests that run against BFDD + bgpd: Tests that run against BGPD + eigrpd: Tests that run against EIGRPD + isisd: Tests that run against ISISD + ldpd: Tests that run against LDPD + nhrpd: Tests that run against NHRPD + ospf6d: Tests that run against OSPF6D + ospfd: Tests that run against OSPFD + pathd: Tests that run against PATHD + pbrd: Tests that run against PBRD + pimd: Tests that run against PIMD + ripd: Tests that run against RIPD + ripngd: Tests that run against RIPNGD + sharpd: Tests that run against SHARPD + staticd: Tests that run against STATICD + vrrpd: Tests that run against VRRPD + [topogen] # Default configuration values # diff --git a/tests/topotests/rip-topo1/test_rip_topo1.py b/tests/topotests/rip-topo1/test_rip_topo1.py index de11b78824..edad1ff65d 100644 --- a/tests/topotests/rip-topo1/test_rip_topo1.py +++ b/tests/topotests/rip-topo1/test_rip_topo1.py @@ -104,7 +104,7 @@ class NetworkTopo(Topo): ## ##################################################### - +@pytest.mark.rip def setup_module(module): global topo, net @@ -352,9 +352,11 @@ def test_zebra_ipv4_routingTable(): else: print("r%s ok" % i) - assert failures == 0, ( - "Zebra IPv4 Routing Table verification failed for router r%s:\n%s" - % (i, diff) + assert ( + failures == 0 + ), "Zebra IPv4 Routing Table verification failed for router r%s:\n%s" % ( + i, + diff, ) # Make sure that all daemons are still running diff --git a/tests/topotests/ripng-topo1/test_ripng_topo1.py b/tests/topotests/ripng-topo1/test_ripng_topo1.py index 2976cdefe4..47b63e5b26 100644 --- a/tests/topotests/ripng-topo1/test_ripng_topo1.py +++ b/tests/topotests/ripng-topo1/test_ripng_topo1.py @@ -104,7 +104,7 @@ class NetworkTopo(Topo): ## ##################################################### - +@pytest.mark.rip def setup_module(module): global topo, net @@ -373,9 +373,11 @@ def test_zebra_ipv6_routingTable(): else: print("r%s ok" % i) - assert failures == 0, ( - "Zebra IPv6 Routing Table verification failed for router r%s:\n%s" - % (i, diff) + assert ( + failures == 0 + ), "Zebra IPv6 Routing Table verification failed for router r%s:\n%s" % ( + i, + diff, ) # Make sure that all daemons are running diff --git a/tests/topotests/simple-snmp-test/r1/bgpd.conf b/tests/topotests/simple-snmp-test/r1/bgpd.conf new file mode 100644 index 0000000000..00d1e17670 --- /dev/null +++ b/tests/topotests/simple-snmp-test/r1/bgpd.conf @@ -0,0 +1,6 @@ +log file /tmp/bgpd.log debugging +! +router bgp 100 + bgp router-id 1.1.1.1 + +agentx diff --git a/tests/topotests/simple-snmp-test/r1/isisd.conf b/tests/topotests/simple-snmp-test/r1/isisd.conf new file mode 100644 index 0000000000..b5ca993da3 --- /dev/null +++ b/tests/topotests/simple-snmp-test/r1/isisd.conf @@ -0,0 +1,46 @@ +log stdout debugging +! +debug isis route-events +debug isis events +! +interface r1-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r1-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r1-eth2 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0001.00 + is-type level-1 + topology ipv6-unicast +! +line vty +! diff --git a/tests/topotests/simple-snmp-test/r1/snmpd.conf b/tests/topotests/simple-snmp-test/r1/snmpd.conf new file mode 100644 index 0000000000..b37911da36 --- /dev/null +++ b/tests/topotests/simple-snmp-test/r1/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:1.1.1.1:161 + +com2sec public 1.1.1.1 public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/simple-snmp-test/r1/zebra.conf b/tests/topotests/simple-snmp-test/r1/zebra.conf new file mode 100644 index 0000000000..5281d0055d --- /dev/null +++ b/tests/topotests/simple-snmp-test/r1/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +interface r1-eth0 + ip address 192.168.12.12/24 + ipv6 address 2000:1:1:12::12/64 +! +interface r1-eth1 + ip address 192.168.13.13/24 + ipv6 address 2000:1:1:13::13/64 +! +interface r1-eth2 + ip address 192.168.14.14/24 + ipv6 address 2000:1:1:14::14/64 +! +! +interface lo + ip address 1.1.1.1/32 + ipv6 address 2000:1:1:1::1/128 +! +! +! +line vty diff --git a/tests/topotests/simple-snmp-test/test_simple_snmp.py b/tests/topotests/simple-snmp-test/test_simple_snmp.py new file mode 100755 index 0000000000..88ff01bf0a --- /dev/null +++ b/tests/topotests/simple-snmp-test/test_simple_snmp.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python + +# +# test_simple_snmp.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# 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_bgp_simple snmp.py: Test snmp infrastructure. +""" + +import os +import sys +import json +from functools import partial +from time import sleep +import pytest + +# 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 +from lib.snmptest import SnmpTester + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("r1") + + # r1-eth0 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + # r1-eth1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + # r1-eth2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + # skip tests is SNMP not installed + if not os.path.isfile("/usr/sbin/snmpd"): + error_msg = "SNMP not installed - skipping" + pytest.skip(error_msg) + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + r1 = tgen.gears["r1"] + + router_list = tgen.routers() + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, "{}/bgpd.conf".format(rname)), + "-M snmp", + ) + router.load_config( + TopoRouter.RD_SNMP, + os.path.join(CWD, "{}/snmpd.conf".format(rname)), + "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap", + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_r1_bgp_version(): + "Wait for protocol convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # tgen.mininet_cli() + r1 = tgen.net.get("r1") + r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") + assert r1_snmp.test_oid("bgpVersin", None) + assert r1_snmp.test_oid("bgpVersion", "10") + assert r1_snmp.test_oid_walk("bgpVersion", ["10"]) + assert r1_snmp.test_oid_walk("bgpVersion", ["10"], ["0"]) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_routing_with_ebgp/static_routes_topo1_ebgp.json b/tests/topotests/static_routing_with_ebgp/static_routes_topo1_ebgp.json new file mode 100644 index 0000000000..7099043adc --- /dev/null +++ b/tests/topotests/static_routing_with_ebgp/static_routes_topo1_ebgp.json @@ -0,0 +1,157 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/static_routing_with_ebgp/static_routes_topo2_ebgp.json b/tests/topotests/static_routing_with_ebgp/static_routes_topo2_ebgp.json new file mode 100644 index 0000000000..91820b0491 --- /dev/null +++ b/tests/topotests/static_routing_with_ebgp/static_routes_topo2_ebgp.json @@ -0,0 +1,363 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link5": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link6": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link7": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + }, + "redistribute": [{ + "redist_type": "static" + }] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link5": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link6": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link7": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + }, + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link5": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link6": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link7": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link5": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link6": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link7": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/static_routing_with_ebgp/static_routes_topo3_ebgp.json b/tests/topotests/static_routing_with_ebgp/static_routes_topo3_ebgp.json new file mode 100644 index 0000000000..5246a311ea --- /dev/null +++ b/tests/topotests/static_routing_with_ebgp/static_routes_topo3_ebgp.json @@ -0,0 +1,189 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 29, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link0": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/static_routing_with_ebgp/static_routes_topo4_ebgp.json b/tests/topotests/static_routing_with_ebgp/static_routes_topo4_ebgp.json new file mode 100644 index 0000000000..bb72578ff3 --- /dev/null +++ b/tests/topotests/static_routing_with_ebgp/static_routes_topo4_ebgp.json @@ -0,0 +1,428 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link1": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link2": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link3": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link1": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link2": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link3": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + } + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "password": "r1" + }, + "r2-link1": { + "password": "r1" + }, + "r2-link2": { + "password": "r1" + }, + "r2-link3": { + "password": "r1" + } + } + }, + "r3": { + "dest_link": { + "r2-link0": { + "password": "r1" + }, + "r2-link1": { + "password": "r1" + }, + "r2-link2": { + "password": "r1" + }, + "r2-link3": { + "password": "r1" + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "password": "r1" + }, + "r2-link1": { + "password": "r1" + }, + "r2-link2": { + "password": "r1" + }, + "r2-link3": { + "password": "r1" + } + } + }, + "r3": { + "dest_link": { + "r2-link0": { + "password": "r1" + }, + "r2-link1": { + "password": "r1" + }, + "r2-link2": { + "password": "r1" + }, + "r2-link3": { + "password": "r1" + } + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link0": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link1": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link2": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link3": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link0": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link1": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link2": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link3": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + } + } + } + } + } + } + } + } + }, + "vm1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "vm2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "vm3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "vm4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "vm5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "vm6": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py new file mode 100644 index 0000000000..a33257de65 --- /dev/null +++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py @@ -0,0 +1,1261 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# +""" + + -Verify static route ECMP functionality with 2 next hop. + + -Verify static route functionality with 2 next hop and different AD + value. + + -Verify RIB status when same route advertise via BGP and static route. + +""" +import sys +import json +import time +import os +import pytest +import platform +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_interfaces_cfg, + shutdown_bringup_interface, + stop_router, + start_router, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/static_routes_topo1_ebgp.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": ["11.0.20.1/32", "11.0.20.2/32"], "ipv6": ["2::1/128", "2::2/128"]} +NETWORK2 = {"ipv4": "11.0.20.1/32", "ipv6": "2::1/128"} + +PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"} + + +class CreateTopo(Topo): + """ + Test CreateTopo - topology 1. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment. + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('These tests will not run. (have kernel "{}", ' + 'requires kernel >= 4.19)'.format(platform.release())) + pytest.skip(error_msg) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def populate_nh(): + NEXT_HOP_IP = { + "nh1": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0], + }, + "nh2": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0], + }, + } + return NEXT_HOP_IP + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_static_route_2nh_p0_tc_1_ebgp(request): + """ + Verify static route ECMP functionality with 2 next hop. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + + step( + "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1" + "(28.1.1.2 ) and N2 (29.1.1.2) , Static route next-hop present on" + "R1" + ) + step("ex :- ip route 10.1.1.1/24 28.1.1.2 & ip route 10.1.1.1/24 29.1.1.1") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + }, + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + }, + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, static route installed in RIB using show ip route" + " with 2 ECMP next hop " + ) + nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Configure IBGP IPv4 peering between R2 and R3 router.") + step("Configure redistribute static in BGP on R2 router") + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove the static route configured with nexthop N1 from running config") + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, after removing the static route with N1 , " + "route become active with nexthop N2 and vice versa." + ) + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes is still present in RIB".format( + tc_name + ) + + nh = [NEXT_HOP_IP["nh2"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("Configure the static route with nexthop N1") + + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove the static route configured with nexthop N2 from" "running config") + + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, after removing the static route with N2 , " + "route become active with nexthop N1 and vice versa." + ) + nh = NEXT_HOP_IP["nh2"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + nh = [NEXT_HOP_IP["nh1"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("Configure the static route with nexthop N2") + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Shut nexthop interface N1") + intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] + + shutdown_bringup_interface(tgen, dut, intf, False) + + step("Only one the nexthops should be active in RIB.") + + nh = NEXT_HOP_IP["nh2"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + dut = "r3" + result = verify_bgp_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + protocol=protocol, + next_hop=nh, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + dut = "r2" + nh = [NEXT_HOP_IP["nh2"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Route is" + " missing in RIB".format(tc_name) + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + dut = "r2" + step("No shut the nexthop interface N1") + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "after shut of nexthop N1 , route become active " + "with nexthop N2 and vice versa." + ) + nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]] + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("Shut nexthop interface N2") + intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] + dut = "r2" + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + " after shut of nexthop N1 , route become active with " + "nexthop N2 and vice versa." + ) + nh = NEXT_HOP_IP["nh2"][addr_type] + + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + nh = [NEXT_HOP_IP["nh1"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Route is" + " missing in RIB".format(tc_name) + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + step("No shut nexthop interface N2") + dut = "r2" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "after shut of nexthop N1 , route become active " + "with nexthop N2 and vice versa." + ) + nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]] + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Route is" + " missing in RIB".format(tc_name) + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + + start_router(tgen, "r2") + + dut = "r2" + step( + "After reload of FRR router , static route installed" + " in RIB and FIB properly ." + ) + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): + """ + Verify static route functionality with 2 next hop & different AD value. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + step( + "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1" + "(28.1.1.2 ) AD 10 and N2 (29.1.1.2) AD 20 , Static route next-hop" + "present on R1 \n ex :- ip route 10.1.1.1/24 28.1.1.2 10 & " + "ip route 10.1.1.1/24 29.1.1.2 20" + ) + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + }, + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + }, + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, static route installed in RIB using " + "show ip route with 2 next hop , lowest AD nexthop is active " + ) + rte1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + nh = [NEXT_HOP_IP["nh1"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "missing in RIB".format(tc_name) + + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + } + ] + } + } + nh = [NEXT_HOP_IP["nh2"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + step("Configure IBGP IPv4 peering between R2 and R3 router.") + step("Explicit route is added in R3 for R2 nexthop rechability") + rt3_rtes = { + "r3": { + "static_routes": [ + { + "network": NEXT_HOP_IP["nh1"][addr_type] + "/32", + "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type], + }, + { + "network": NEXT_HOP_IP["nh2"][addr_type] + "/32", + "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type], + }, + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, rt3_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Configure redistribute static in BGP on R2 router") + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove the static route configured with nexthop N1 from" "running config") + rt1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, rt1_nh1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, after removing the static route with N1 , " + "route become active with nexthop N2 and vice versa." + ) + rte1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + nh = [NEXT_HOP_IP["nh1"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + rte1_nh1, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "missing in RIB".format(tc_name) + + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + } + ] + } + } + nh = [NEXT_HOP_IP["nh2"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + step("Configure the static route with nexthop N1") + rte1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, rte1_nh1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove the static route configured with nexthop N2 from" "running config") + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, rte2_nh2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, after removing the static route with N2 , " + "route become active with nexthop N1 and vice versa." + ) + nh = NEXT_HOP_IP["nh2"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + nh = [NEXT_HOP_IP["nh1"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("Configure the static route with nexthop N2") + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, rte2_nh2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Shut nexthop interface N1") + intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] + + shutdown_bringup_interface(tgen, dut, intf, False) + + step("after shut of nexthop N1 , route become active with nexthop N2") + + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + rte1_nh1, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + nh = [NEXT_HOP_IP["nh2"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("No shut the nexthop interface N1") + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "after shut of nexthop N1 , route become active " + "with nexthop N2 and vice versa." + ) + nh = [NEXT_HOP_IP["nh1"][addr_type]] + + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("Shut nexthop interface N2") + intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] + + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + " after shut of nexthop N1 , route become active with " + "nexthop N2 and vice versa." + ) + nh = NEXT_HOP_IP["nh2"][addr_type] + + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + nh = [NEXT_HOP_IP["nh1"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("No shut nexthop interface N2") + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "after shut of nexthop N1 , route become active " + "with nexthop N2 and vice versa." + ) + rte1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + nh = [NEXT_HOP_IP["nh1"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "missing in RIB".format(tc_name) + + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + } + ] + } + } + nh = [NEXT_HOP_IP["nh2"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + dut = "r3" + protocol = "bgp" + + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + dut = "r2" + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + + start_router(tgen, "r2") + + step( + "After reload of FRR router , static route installed" + " in RIB and FIB properly ." + ) + rte1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + nh = [NEXT_HOP_IP["nh1"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "missing in RIB".format(tc_name) + + dut = "r3" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "missing in RIB".format(tc_name) + + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + } + ] + } + } + nh = [NEXT_HOP_IP["nh2"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + dut = "r3" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, rte2_nh2, next_hop=nh) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_same_rte_from_bgp_static_p0_tc5_ebgp(request): + """ + Verify RIB status when same route advertise via BGP and static + route + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + NEXT_HOP_IP = populate_nh() + step("Configure EBGP IPv4 peering between R2 and R3 router.") + + step( + "Configure IPv4 static route (10.1.1.1/24) in R2 with next hop" + "N1 (28.1.1.2 ) and N2 (29.1.1.2) , Static route next-hop present" + "on R1" + ) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + }, + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + }, + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure redistribute static in BGP.") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Verify on R3 , route receive on R3 BGP table ") + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + step("Verify route installed in the RIB and FIB of R3") + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + step( + "Configure 2 links/interfaces between R1 and R3 , keep one" + "interface in shut (active) state and other interface in no shut" + "(inactive) state" + ) + dut = "r3" + intf = topo["routers"]["r3"]["links"]["r1-link0"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + "Configure same static route (10.1.1.1/24) in R3 with inactive" + "nexthop interface" + ) + + step( + "Configure same static route 10.1.1.1/24) again in R3 with" + "active nexthop interface" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r1"]["links"]["r3-link0"][ + addr_type + ], + }, + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r1"]["links"]["r3-link1"][ + addr_type + ], + }, + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify when static route configure with inactive nexthop , " + "verify BGP received route is active in the RIB and FIB" + ) + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Route is" + " missing in BGP RIB".format(tc_name) + + protocol = "bgp" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route is" + " missing in RIB".format(tc_name) + + step("Remove the static route on R3 configured with active" "interface") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r1"]["links"]["r3-link0"][ + addr_type + ], + "delete": True, + }, + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r1"]["links"]["r3-link1"][ + addr_type + ], + "delete": True, + }, + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "After removing the static route with active nexthop verify " + "BGP received route is became active in RIB and FIB" + ) + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Route is" + " missing in BGP RIB".format(tc_name) + + protocol = "bgp" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route is" + " missing in RIB".format(tc_name) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py new file mode 100644 index 0000000000..93320df327 --- /dev/null +++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py @@ -0,0 +1,1711 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + + +""" + -Verify static route functionality with 8 next hop different AD value + and BGP ECMP + + -Verify 8 static route functionality with 8 next hop different AD + + -Verify static route with 8 next hop with different AD value and 8 + EBGP neighbors + + -Verify static route with 8 next hop with different AD value and 8 + IBGP neighbors + + -Delete the static route and verify the RIB and FIB state + + -Verify 8 static route functionality with 8 ECMP next hop +""" +import sys +import json +import time +import os +import pytest +import platform +import random +from lib.topotest import version_cmp + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + shutdown_bringup_interface, + stop_router, + start_router, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/static_routes_topo2_ebgp.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + "11.0.20.6/32", + "11.0.20.7/32", + "11.0.20.8/32", + ], + "ipv6": [ + "2::1/128", + "2::2/128", + "2::3/128", + "2::4/128", + "2::5/128", + "2::6/128", + "2::7/128", + "2::8/128", + ], +} +PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"} +PREFIX2 = {"ipv4": "110.0.20.2/32", "ipv6": "20::2/128"} +NEXT_HOP_IP = [] +topo_diag = """ + Please view in a fixed-width font such as Courier. + +------+ +------+ +------+ + | +--------------+ +--------------+ | + | | | | | | + | R1 +---8 links----+ R2 +---8 links----+ R3 | + | | | | | | + | +--------------+ +--------------+ | + +------+ +------+ +------+ + +""" + + +class CreateTopo(Topo): + """ + Test CreateTopo - topology 1. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + + Set up the pytest environment. + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('These tests will not run. (have kernel "{}", ' + 'requires kernel >= 4.19)'.format(platform.release())) + pytest.skip(error_msg) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def populate_nh(): + NEXT_HOP_IP = { + "nh1": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0], + }, + "nh2": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0], + }, + "nh3": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0], + }, + "nh4": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link3"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link3"]["ipv6"].split("/")[0], + }, + "nh5": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link4"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link4"]["ipv6"].split("/")[0], + }, + "nh6": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link5"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link5"]["ipv6"].split("/")[0], + }, + "nh7": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link6"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link6"]["ipv6"].split("/")[0], + }, + "nh8": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link7"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link7"]["ipv6"].split("/")[0], + }, + } + return NEXT_HOP_IP + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request): + """ + Verify 8 static route functionality with 8 ECMP next hop + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + NEXT_HOP_IP = populate_nh() + step("Configure 8 interfaces / links between R1 and R2") + step("Configure 8 interfaces / links between R2 and R3") + step("Configure 8 IBGP IPv4 peering between R2 and R3 router.") + reset_config_on_routers(tgen) + + step( + "Configure 8 IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) , N2(22.1.1.2) , N3(23.1.1.2) , N4(24.1.1.2) ," + "N5(25.1.1.2) , N6(26.1.1.2) , N7(27.1.1.2) , N8(28.1.1.2) ," + "Static route next-hop present on R1" + ) + nh_all = {} + for addr_type in ADDR_TYPES: + # Enable static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + nh_all[addr_type] = [ + NEXT_HOP_IP["nh1"][addr_type], + NEXT_HOP_IP["nh2"][addr_type], + NEXT_HOP_IP["nh3"][addr_type], + NEXT_HOP_IP["nh4"][addr_type], + NEXT_HOP_IP["nh5"][addr_type], + NEXT_HOP_IP["nh6"][addr_type], + NEXT_HOP_IP["nh7"][addr_type], + NEXT_HOP_IP["nh8"][addr_type], + ] + + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r3" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + dut = "r2" + protocol = "static" + step( + "After removing the static route with N1 to N8 one by one , " + "verify that entry is removed from RIB and FIB of R3 " + ) + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one , " + "verify that entry is removed from RIB and FIB of R3 " + ) + nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed\nError: Routes is" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by one") + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed\nError: Routes are" + " missing in RIB".format(tc_name) + + protocol = "static" + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + # Shutdown interface + dut = "r2" + step( + " interface which is about to be shut no shut between r1 and r2 is " "%s", + topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"], + ) + intf = topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step("Random no shut of the nexthop interfaces") + # Bringup interface + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "After random shut/no shut of nexthop , only that " + "nexthop deleted/added from all the routes , other nexthop remain " + "unchanged" + ) + dut = "r2" + protocol = "static" + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Remove random static route with all the nexthop") + dut = "r2" + randnum = random.randint(1, 7) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type], + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After delete of random route , that route only got deleted from" + " RIB/FIB other route are showing properly" + ) + nh = NEXT_HOP_IP["nh{}".format(randnum)][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type], + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + start_router(tgen, "r2") + + step( + "After reload of FRR router , static route " + "installed in RIB and FIB properly ." + ) + for addr_type in ADDR_TYPES: + # Enable static routes + nhp = 1 + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + logger.info("Verifying %s routes on r2", addr_type) + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Remove the redistribute static knob") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static", "delete": True} + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the BGP neighbor or redistribute static knob , " + "verify route got clear from RIB and FIB of R3 routes " + ) + dut = "r3" + protocol = "bgp" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request): + """ + Verify static route functionality with 8 next hop different AD + value and BGP ECMP + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure 8 interfaces / links between R1 and R2 ,") + step("Configure 8 interlaces/links between R2 and R3") + step( + "Configure IBGP IPv4 peering over loopback interface between" + "R2 and R3 router." + ) + step("Configure redistribute static in BGP on R2 router") + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + nh_all = {} + for addr_type in ADDR_TYPES: + nh_all[addr_type] = [] + for nhp in range(1, 9): + nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30," + "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60," + "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop" + "present on R1" + ) + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + "On R2, static route installed in RIB using " + "show ip route with 8 next hop , lowest AD nexthop is active" + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + wait=2, + attempts=3, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one , " + "route become active with next preferred nexthop and nexthop which " + "got removed is not shown in RIB and FIB" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by one") + + for addr_type in ADDR_TYPES: + # add static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + " After configuring them, route is always active with lowest AD" + " value and all the nexthop populated in RIB and FIB again" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + wait=2, + attempts=3, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + input_dict_5 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_5, + next_hop=nhip, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + + step("Random no shut of the nexthop interfaces") + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + dut = "r2" + protocol = "static" + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {}: Failed \n " "Error: Routes are missing in RIB".format(tc_name) + + protocol = "bgp" + dut = "r3" + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {}: Failed \n " "Error: Routes are missing in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + + start_router(tgen, "r2") + + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, ( + "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + ) + + + write_test_footer(tc_name) + + +def test_static_route_8nh_diff_AD_ebgp_ecmp_p1_tc8_ebgp(request): + """ + Verify static route with 8 next hop with different AD value and 8 + EBGP neighbors + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure 8 interfaces / links between R1 and R2") + step("Configure 8 interlaces/links between R2 and R3") + step("Configure 8 EBGP IPv4 peering between R2 and R3") + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30," + "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60," + "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop" + "present on R1" + ) + nh_all = {} + for addr_type in ADDR_TYPES: + nh_all[addr_type] = [] + for nhp in range(1, 9): + nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + "On R2, static route installed in RIB using " + "show ip route with 8 next hop , lowest AD nexthop is active" + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one , " + "route become active with next preferred nexthop and nexthop which " + "got removed is not shown in RIB and FIB" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by one") + + for addr_type in ADDR_TYPES: + # add static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + " After configuring them, route is always active with lowest AD" + " value and all the nexthop populated in RIB and FIB again" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + input_dict_5 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_5, + next_hop=nhip, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + + step("Random no shut of the nexthop interfaces") + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + dut = "r2" + protocol = "static" + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + + start_router(tgen, "r2") + + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, ( + "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + ) + + write_test_footer(tc_name) + + +def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ebgp(request): + """ + Verify 8 static route functionality with 8 next hop different AD' + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + NEXT_HOP_IP = populate_nh() + + step("Configure 8 interfaces / links between R1 and R2 ") + step("Configure 8 IBGP IPv4 peering between R2 and R3 router.") + reset_config_on_routers(tgen) + + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30," + "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60," + "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80" + ) + step( + "Configure nexthop AD in such way for static route S1 , N1 is" + "preferred and for S2 , N2 is preferred and so on .." + ) + nh_all = {} + for addr_type in ADDR_TYPES: + nh_all[addr_type] = [] + for nhp in range(1, 9): + nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + second_rte = { + "r2": { + "static_routes": [ + { + "network": PREFIX2[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, second_rte) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + "On R2, static route installed in RIB using " + "show ip route with 8 next hop , lowest AD nexthop is active" + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Verify that highest AD nexthop are inactive") + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + wait=2, + attempts=3, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one , " + "route become active with next preferred nexthop and nexthop which" + "got removed is not shown in RIB and FIB" + ) + + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by one") + for addr_type in ADDR_TYPES: + # add static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + " After configuring them, route is always active with lowest AD" + " value and all the nexthop populated in RIB and FIB again" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type],}]}} + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route with " + "lowest AD is missing in RIB".format(tc_name) + + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + input_dict_5 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_5, + next_hop=nhip, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + + step("Random no shut of the nexthop interfaces") + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + step("Remove random static route with all the nexthop") + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one , " + "route become active with next preferred nexthop and nexthop " + "which got removed is not shown in RIB and FIB" + ) + nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Route " + " is still present in RIB".format(tc_name) + + step("Reconfigure the deleted routes and verify they are installed") + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, "Testcase {} : Failed \nError: Route " + " is still present in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + + start_router(tgen, "r2") + + step("After reloading, verify that routes are still present in R2.") + result = verify_rib( + tgen, + addr_type, + dut, + second_rte, + next_hop=nh, + protocol=protocol, + fib=True, + ) + assert result is True, ( + "Testcase {} : Failed \nError: Route " + " is missing in RIB".format(tc_name) + ) + + write_test_footer(tc_name) + + +def test_static_route_delete_p0_tc11_ebgp(request): + """ + Delete the static route and verify the RIB and FIB state + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + NEXT_HOP_IP = populate_nh() + + step("Configure 8 interfaces / links between R1 and R2 ") + step("Configure 8 IBGP IPv4 peering between R2 and R3 router.") + reset_config_on_routers(tgen) + + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30," + "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60," + "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80" + ) + step( + "Configure nexthop AD in such way for static route S1 , N1 is" + "preferred and for S2 , N2 is preferred and so on .." + ) + nh_all = {} + for addr_type in ADDR_TYPES: + nh_all[addr_type] = [] + for nhp in range(1, 9): + nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + second_rte = { + "r2": { + "static_routes": [ + { + "network": PREFIX2[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, second_rte) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + "On R2, static route installed in RIB using " + "show ip route with 8 next hop , lowest AD nexthop is active" + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Verify that highest AD nexthop are inactive") + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove the redistribute static knob") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static", "delete": True} + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify after removing the redistribute static from BGP all the" + "routes got delete from RIB and FIB of R3 " + ) + + dut = "r3" + protocol = "bgp" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + second_rte = { + "r2": { + "static_routes": [ + { + "network": PREFIX2[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, second_rte) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + " After removing all the routes and nexthop from R2 , " + " verify R2 RIB and FIB is cleared" + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still active in RIB".format(tc_name) + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py new file mode 100644 index 0000000000..255bb073b4 --- /dev/null +++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py @@ -0,0 +1,1333 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# +""" + -Verify static route ECMP functionality with 8 next hop + + -Verify static route functionality with 8 next hop different AD value + + -Verify static route with tag option + + -Verify BGP did not install the static route when it receive route + with local next hop + +""" +import sys +import json +import time +import os +import pytest +import random +import platform +from lib.topotest import version_cmp + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_interfaces_cfg, + shutdown_bringup_interface, + stop_router, + start_router, + create_route_maps, + verify_ip_nht, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/static_routes_topo3_ebgp.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + "11.0.20.6/32", + "11.0.20.7/32", + "11.0.20.8/32", + ], + "ipv6": [ + "2::1/128", + "2::2/128", + "2::3/128", + "2::4/128", + "2::5/128", + "2::6/128", + "2::7/128", + "2::8/128", + ], +} +PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"} +NETWORK2 = {"ipv4": ["11.0.20.1/32"], "ipv6": ["2::1/128"]} +NEXT_HOP_IP = [] + + +class CreateTopo(Topo): + """ + Test CreateTopo - topology 1. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + + Set up the pytest environment. + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + 'These tests will not run. (have kernel "{}", ' + "requires kernel >= 4.19)".format(platform.release()) + ) + pytest.skip(error_msg) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def populate_nh(): + NEXT_HOP_IP = { + "nh1": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0], + }, + "nh2": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0], + }, + "nh3": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0], + }, + "nh4": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link3"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link3"]["ipv6"].split("/")[0], + }, + "nh5": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link4"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link4"]["ipv6"].split("/")[0], + }, + "nh6": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link5"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link5"]["ipv6"].split("/")[0], + }, + "nh7": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link6"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link6"]["ipv6"].split("/")[0], + }, + "nh8": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link7"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link7"]["ipv6"].split("/")[0], + }, + } + return NEXT_HOP_IP + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_staticroute_with_ecmp_p0_tc3_ebgp(request): + """ + Verify static route ECMP functionality with 8 next hop' + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + + step("Configure 8 interfaces / links between R1 and R2,") + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2), N2(22.1.1.2), N3(23.1.1.2), N4(24.1.1.2)," + "N5(25.1.1.2), N6(26.1.1.2), N7(27.1.1.2),N8(28.1.1.2), Static" + "route next-hop present on R1" + ) + + step("Configure IBGP IPv4 peering between R2 and R3 router.") + + for addr_type in ADDR_TYPES: + # Enable static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + nh = [ + NEXT_HOP_IP["nh1"][addr_type], + NEXT_HOP_IP["nh2"][addr_type], + NEXT_HOP_IP["nh3"][addr_type], + NEXT_HOP_IP["nh4"][addr_type], + NEXT_HOP_IP["nh5"][addr_type], + NEXT_HOP_IP["nh6"][addr_type], + NEXT_HOP_IP["nh7"][addr_type], + NEXT_HOP_IP["nh8"][addr_type], + ] + + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by" "one") + + for addr_type in ADDR_TYPES: + # add static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + input_dict_5 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_5, + next_hop=nhip, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + + step("Random no shut of the nexthop interfaces") + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + start_router(tgen, "r2") + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ebgp(request): + """ + Verify static route ECMP functionality with 8 next hop + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + + step("Configure 8 interfaces / links between R1 and R2,") + step("Configure IBGP IPv4 peering between R2 and R3 router.") + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + nh_all = {} + for addr_type in ADDR_TYPES: + nh_all[addr_type] = [] + for nhp in range(1, 9): + nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30," + "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60," + "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop" + "present on R1" + ) + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + "On R2, static route installed in RIB using " + "show ip route with 8 next hop, lowest AD nexthop is active" + ) + step("On R2, static route with lowest AD nexthop installed in FIB") + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route with " + " lowest AD is missing in RIB".format(tc_name) + + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " with high AD are active in RIB".format(tc_name) + + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + + logger.info("Configuring redistribute static") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After configuring them, route is always active with lowest AD" + "value and all the nexthop populated in RIB and FIB again " + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route with " + " lowest AD is missing in RIB".format(tc_name) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one, " + "route become active with next preferred nexthop and nexthop which " + "got removed is not shown in RIB and FIB" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by" "one") + for addr_type in ADDR_TYPES: + # add static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("On R2, static route with lowest AD nexthop installed in FIB") + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route with " + " lowest AD is missing in RIB".format(tc_name) + + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " with high AD are active in RIB".format(tc_name) + + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + input_dict_5 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_5, + next_hop=nhip, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + + step("Random no shut of the nexthop interfaces") + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + start_router(tgen, "r2") + + step( + "After reload of FRR router, static route installed " + "in RIB and FIB properly ." + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route with " + " lowest AD is missing in RIB".format(tc_name) + + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " with high AD are active in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_bgp_local_nexthop_p1_tc14_ebgp(request): + """ + Verify BGP did not install the static route when it receive route + with local next hop + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + step("Configure BGP IPv4 session between R2 and R3") + step("Configure IPv4 static route on R2") + reset_config_on_routers(tgen) + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r3"]["links"]["r2-link0"][ + addr_type + ].split("/")[0], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure redistribute static in the BGP") + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify R2 BGP table has IPv4 route") + dut = "r2" + result = verify_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB of R2".format(tc_name) + + step(" Verify route did not install in the R3 BGP table, RIB/FIB") + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4, expected=False) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in BGP RIB of R2".format(tc_name) + + result = verify_rib(tgen, addr_type, dut, input_dict_4, expected=False) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB of R2".format(tc_name) + + write_test_footer(tc_name) + + +def test_frr_intf_name_as_gw_gap_tc4_ebgp_p0(request): + """ + Verify static route configure with interface name as gateway' + 'address' + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"] + nh = topo["routers"]["r1"]["links"]["r2-link0"] + ip_list = { + "ipv4": [(dut, intf, ["1.1.1.1/32"], nh["ipv4"].split("/")[0])], + "ipv6": [(dut, intf, ["4001::32/128"], nh["ipv6"].split("/")[0])], + } + + step( + "Configure IPv4 and IPv6 static route in FRR with different next" + "hop (ens224 as nexthop))" + ) + step("ip route 2.2.2.0/24 20.1.1.1 ens224 ----from FRR cli") + step("ipv6 route 2000::1/120 5000::1 ens224 ----from FRR cli") + + for addr_type in ADDR_TYPES: + # Enable static routes + nh = topo["routers"]["r2"]["links"]["r1-link0"][addr_type].split("/")[0] + input_dict_4 = { + "r1": { + "static_routes": [ + {"network": ip_list[addr_type][0][2][0], "next_hop": nh} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "IPv4 and IPv6 Static route added in FRR verify using " + "show ip route , nexthop is resolved using show nht" + ) + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, next_hop=nh + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + input_dict_nh = { + "r1": { + nh: { + "Address": nh, + "resolvedVia": "connected", + "nexthops": {"nexthop1": {"Interfcae": intf}}, + } + } + } + result = verify_ip_nht(tgen, input_dict_nh) + assert result is True, "Testcase {} : Failed \nError: Nexthop is" + " missing in RIB".format(tc_name) + + step( + "Shut / no shut IPv4 and IPv6 static next hop interface from" + "kernel and FRR CLI" + ) + + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + "After shut of nexthop interface, IPv4 and IPv6 route got removed " + "from RIB verify using show ip route show nht" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + protocol=protocol, + next_hop=nh, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "After no shut route got added again in RIB /FIB using " + "show ip route nexthop is resolved using show nht" + ) + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, "Testcase {} : Failed".format(tc_name) + + for addr_type in ADDR_TYPES: + nh = topo["routers"]["r2"]["links"]["r1-link0"][addr_type].split("/")[0] + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": ip_list[addr_type][0][2][0], + "next_hop": nh, + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Removing FRR configured static route verify FRR route also " + "removed from FRR" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + protocol=protocol, + next_hop=nh, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes" + " still present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_static_route_with_tag_p0_tc_13_ebgp(request): + """ + Verify static route with tag option + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure 8 links between R1 and R2") + step("Configure 1 links between R2 and R3") + NEXT_HOP_IP = populate_nh() + + step( + "Configure 2 IPv4 static route (S1 and S2) in R2 with same" + "next hop N1 28.1.1.2" + ) + step("Configure static route S1 with tag 1 and static route S2 with" "tag2") + step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 1") + step("S2= ip route 20.1.1.1/24 28.1.1.2 tag 2") + step("Enable redistribute static in BGP with route-map") + reset_config_on_routers(tgen) + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "tag": 4001, + }, + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "tag": 4002, + }, + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("verify routes are present in RIB") + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Configure route-map on R2 with allow tag1 and deny tag2") + + # Create route map + input_dict_3 = { + "r2": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": {addr_type: {"tag": "4001"}}, + }, + { + "action": "deny", + "seq_id": 20, + "match": {addr_type: {"tag": "4002"}}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + } + ] + } + } + } + }, + "redistribute": [{"redist_type": "static"}], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify static route S1 advetised in BGP table when tag1 permit" + "in route-map else it is denied" + ) + dut = "r3" + input_dict_0 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "tag": 4002, + } + ] + } + } + + result = verify_rib( + tgen, addr_type, dut, input_dict_0, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route with " + "tag 4002 is still present in RIB".format(tc_name) + + dut = "r2" + input_dict_1 = { + "r2": {"static_routes": [{"network": NETWORK[addr_type], "tag": 4001}]} + } + + result = verify_rib(tgen, addr_type, dut, input_dict_0, protocol=protocol) + assert result is True, "Testcase {} : Failed \nError: Route with " + "tag 4001 is missing in RIB".format(tc_name) + + step("Modify the route-map to allow tag2 and deny tag1") + # Create route map + input_dict_3 = { + "r2": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + { + "action": "deny", + "seq_id": 10, + "match": {addr_type: {"tag": "4001"}}, + }, + { + "action": "permit", + "seq_id": 20, + "match": {addr_type: {"tag": "4002"}}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r3" + step( + "Verify static route S2 advertised in BGP table when tag2" + "permit in route-map else it is denied" + ) + protocol = "bgp" + input_dict_0 = { + "r2": {"static_routes": [{"network": NETWORK2[addr_type], "tag": 4002}]} + } + + result = verify_rib(tgen, addr_type, dut, input_dict_0, protocol=protocol) + assert result is True, "Testcase {} : Failed \nError: Route with " + "tag 4002 is missing in RIB".format(tc_name) + + input_dict_1 = { + "r2": {"static_routes": [{"network": NETWORK[addr_type], "tag": 4001}]} + } + result = verify_rib( + tgen, addr_type, dut, input_dict_1, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route with " + "tag 4001 is still present in RIB".format(tc_name, result) + + step("Configure one static route with 2 ECMP nexthop N1 and N2") + step("For N1 configure tag 1 and for N2 configure tag 2") + step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 1") + step("S1= ip route 10.1.1.1/24 29.1.1.2 tag 2") + step("configure the route-map to allow tag1 and deny tag 2") + step("Modify the route-map to allow tag2 and deny tag1") + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "tag": 4001, + }, + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "tag": 4002, + }, + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("shut/no shut of tag1 and tag2 nexthop") + + intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + shutdown_bringup_interface(tgen, dut, intf, True) + + step("configure one static route with 3 next-hop") + step("N1-tag1, N2-tag2, N3-tag3") + step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 1") + step("S1= ip route 10.1.1.1/24 29.1.1.2 tag 2") + step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 3") + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "tag": 4001, + }, + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "tag": 4002, + }, + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh3"][addr_type], + "tag": 4003, + }, + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "static" + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + + step("configure the route-map to allow tag2 & tag3 and deny tag1") + # Create route map + input_dict_3 = { + "r2": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + { + "action": "deny", + "seq_id": 10, + "match": {addr_type: {"tag": "4001"}}, + }, + { + "action": "permit", + "seq_id": 20, + "match": {addr_type: {"tag": "4002"}}, + }, + { + "action": "permit", + "seq_id": 30, + "match": {addr_type: {"tag": "4003"}}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify static route advertised in BGP table with tag3" + " nexthop if tag2 is down" + ) + dut = "r3" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("shut / no shut of tag2 and tag3 next-hop") + + intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + intf = topo["routers"]["r2"]["links"]["r1-link2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step("shut/no shut of tag2 and tag3 nexthop") + intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + + intf = topo["routers"]["r2"]["links"]["r1-link2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + + step("Verify after shut/noshut of nexthop BGP table updated correctly") + dut = "r3" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py new file mode 100644 index 0000000000..75657a8895 --- /dev/null +++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py @@ -0,0 +1,994 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +""" + +Following tests are covered in the script. + +- Verify static route are blocked from route-map and prefix-list + applied in BGP nbrs +- Verify Static route when FRR connected to 2 TOR +""" + +import sys +import json +import time +import os +import pytest +import platform +import ipaddress +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + check_address_types, + step, + create_prefix_lists, + create_route_maps, + create_interfaces_cfg, + verify_prefix_lists, + verify_route_maps, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp_and_verify, + clear_bgp, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/static_routes_topo4_ebgp.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"} +NEXT_HOP_IP = {} + + +class CreateTopo(Topo): + """ + Test CreateTopo - topology 1. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Set up the pytest environment. + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('These tests will not run. (have kernel "{}", ' + 'requires kernel >= 4.19)'.format(platform.release())) + pytest.skip(error_msg) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### +def static_routes_rmap_pfxlist_p0_tc7_ebgp(request): + """ + Verify static route are blocked from route-map & prefix-list applied in BGP + nbrs + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step("Configure holddown timer = 1 keep alive = 3 in all the neighbors") + step("verify bgp convergence before starting test case") + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step( + "Configure 4 IPv4 and 4 IPv6 nbrs with password with mismatch " + " authentication between FRR routers " + ) + + for addr_type in ADDR_TYPES: + # Api call to modfiy BGP timerse + input_dict = { + "r2": { + "bgp": { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": {"password": "r2"}, + "r2-link1": {"password": "r2"}, + "r2-link2": {"password": "r2"}, + "r2-link3": {"password": "r2"}, + } + }, + "r3": { + "dest_link": { + "r2-link0": {"password": "r2"}, + "r2-link1": {"password": "r2"}, + "r2-link2": {"password": "r2"}, + "r2-link3": {"password": "r2"}, + } + }, + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + clear_bgp(tgen, addr_type, "r2") + + step(" All BGP nbrs are down as authentication is mismatch on both" " the sides") + + bgp_convergence = verify_bgp_convergence(tgen, topo, expected=False) + assert bgp_convergence is not True, "Testcase {} : " + "Failed \n BGP nbrs must be down. Error: {}".format(tc_name, bgp_convergence) + + step( + "Configure 4 IPv4 and 4 IPv6 nbrs with macthing password " + " authentication between FRR routers " + ) + for addr_type in ADDR_TYPES: + input_dict = { + "r2": { + "bgp": { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": {"password": "r1"}, + "r2-link1": {"password": "r1"}, + "r2-link2": {"password": "r1"}, + "r2-link3": {"password": "r1"}, + } + }, + "r3": { + "dest_link": { + "r2-link0": {"password": "r1"}, + "r2-link1": {"password": "r1"}, + "r2-link2": {"password": "r1"}, + "r2-link3": {"password": "r1"}, + } + }, + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, deepcopy(input_dict)) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("All BGP nbrs are up as authentication is matched now") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n " "Error: {}".format( + tc_name, bgp_convergence + ) + + step("Create prefix list P1 to permit VM3 & deny VM1 v4 & v6 routes") + step("Create prefix list P2 to permit VM6 IPv4 and IPv6 routes") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "prefix_lists": { + addr_type: { + "pf_list_1_{}".format(addr_type): [ + { + "seqid": 10, + "network": topo["routers"]["r2"]["links"]["vm3"][ + addr_type + ], + "action": "permit", + }, + { + "seqid": 20, + "network": topo["routers"]["r2"]["links"]["vm1"][ + addr_type + ], + "action": "deny", + }, + ], + "pf_list_2_{}".format(addr_type): [ + { + "seqid": 10, + "network": topo["routers"]["r2"]["links"]["vm6"][ + addr_type + ], + "action": "permit", + } + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Prefix list created with matching networks deny or permit " + "show ip prefix list" + ) + result = verify_prefix_lists(tgen, input_dict_2) + assert result is not True, "Testcase {} : Failed \n" + " Error: {}".format(tc_name, result) + + step("Redistribute all the routes (connected, static)") + input_dict_2_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2_r2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2_r3 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute connected in Router BGP") + + input_dict_2_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2_r3 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply prefix list P1 on BGP neighbors 1 2 3 4 connected from " "frr r1") + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link1": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link2": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link3": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply prefix list P2 on BGP nbrs 5 & 6 connected from FRR-2") + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link1": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link2": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link3": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + clear_bgp_and_verify(tgen, topo, "r2") + + step( + "VM1 IPv4 and IPv6 Route which is denied using prefix list is " + "not present on FRR1 side routing table , also not able to " + "ping the routes show ip route" + ) + + dut = "r1" + protocol = "bgp" + ntwk_r2_vm1 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r2"]["links"]["vm1"][addr_type]) + ).network + ) + input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}} + result4 = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result4 is not True, "Testcase {} : Failed , VM1 route is " + "not filtered out via prefix list. \n Error: {}".format(tc_name, result4) + + step( + "VM4 and VM6 IPV4 and IPv6 address are present in local and " + "FRR2 routing table show ip bgp show ip route" + ) + + dut = "r2" + ntwk_r2_vm6 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r2"]["links"]["vm6"][addr_type]) + ).network + ) + input_dict = {"r3": {"static_routes": [{"network": ntwk_r2_vm6}]}} + result4 = verify_rib(tgen, addr_type, dut, input_dict) + assert result4 is True, "Testcase {} : Failed.\n Error: {}".format( + tc_name, result4 + ) + + step("Remove prefix list from all the neighbors") + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link1": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link2": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link3": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link1": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link2": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link3": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + clear_bgp_and_verify(tgen, topo, "r2") + + step("Create RouteMap_1 with prefix list P1 and weight 50") + # Create route map + rmap_dict = { + "r2": { + "route_maps": { + "rmap_pf_list_1_{}".format(addr_type): [ + { + "action": "permit", + "set": {"weight": 50}, + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, rmap_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Create RouteMap_2 with prefix list P2 and weight 50") + # Create route map + rmap_dict = { + "r2": { + "route_maps": { + "rmap_pf_list_2_{}".format(addr_type): [ + { + "action": "permit", + "set": {"weight": 50}, + "match": { + addr_type: { + "prefix_lists": "pf_list_2_{}".format(addr_type) + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, rmap_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify Route-map created verify using show route-map") + # verify rmap_pf_list_1 and rmap_pf_list_2 are present in router r2 + input_dict = { + "r2": { + "route_maps": [ + "rmap_pf_list_1_{}".format(addr_type), + "rmap_pf_list_2_{}".format(addr_type), + ] + } + } + result = verify_route_maps(tgen, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply policy RouteMap_1 nbrs 1 2 3 4 to FRR 1") + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "route_maps": [ + { + "name": "rmap_pf_list_1_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link1": { + "route_maps": [ + { + "name": "rmap_pf_list_1_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link2": { + "route_maps": [ + { + "name": "rmap_pf_list_1_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link3": { + "route_maps": [ + { + "name": "rmap_pf_list_1_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply policy RouteMap_2 nbrs 5 and 6 to FRR2") + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "route_maps": [ + { + "name": "rmap_pf_list_2_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link1": { + "route_maps": [ + { + "name": "rmap_pf_list_2_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link2": { + "route_maps": [ + { + "name": "rmap_pf_list_2_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link3": { + "route_maps": [ + { + "name": "rmap_pf_list_2_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After applying to BGP neighbors verify VM1 IPv4 and IPv6 Route" + " which is denied using prefix list is not present on FRR side" + " routing table , also not able to ping the routes show ip route" + " and VM4 and VM6 IPV4 and IPv6 address are present in local and" + " FRR routing table show ip bgp show ip route" + ) + + dut = "r1" + protocol = "bgp" + ntwk_r2_vm1 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r2"]["links"]["vm1"][addr_type]) + ).network + ) + input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}} + result4 = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result4 is not True, "Testcase {} : Failed \n" "Error: {}".format( + tc_name, result4 + ) + + step("vm4 should be present in FRR1") + dut = "r1" + ntwk_r2_vm1 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r1"]["links"]["vm4"][addr_type]) + ).network + ) + input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}} + result4 = verify_rib(tgen, addr_type, dut, input_dict) + assert result4 is True, "Testcase {} : Failed , VM1 route is " + "not filtered out via prefix list. \n Error: {}".format(tc_name, result4) + + step("vm4 should be present in FRR2") + dut = "r2" + ntwk_r2_vm1 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r1"]["links"]["vm4"][addr_type]) + ).network + ) + input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}} + result4 = verify_rib(tgen, addr_type, dut, input_dict) + assert result4 is True, "Testcase {} : Failed , VM1 route is " + "not filtered out via prefix list. \n Error: {}".format(tc_name, result4) + + dut = "r3" + protocol = "bgp" + ntwk_r2_vm6 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r2"]["links"]["vm6"][addr_type]) + ).network + ) + input_dict = {"r3": {"static_routes": [{"network": ntwk_r2_vm6}]}} + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed.\n Error: {}".format( + tc_name, result4 + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_routing_with_ibgp/static_routes_topo1_ibgp.json b/tests/topotests/static_routing_with_ibgp/static_routes_topo1_ibgp.json new file mode 100644 index 0000000000..99b197366a --- /dev/null +++ b/tests/topotests/static_routing_with_ibgp/static_routes_topo1_ibgp.json @@ -0,0 +1,157 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/static_routing_with_ibgp/static_routes_topo2_ibgp.json b/tests/topotests/static_routing_with_ibgp/static_routes_topo2_ibgp.json new file mode 100644 index 0000000000..47596a0a1a --- /dev/null +++ b/tests/topotests/static_routing_with_ibgp/static_routes_topo2_ibgp.json @@ -0,0 +1,371 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + + + "dest_link": { + "r2-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link5": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link6": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link7": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + }, + "redistribute": [{ + "redist_type": "static" + }] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + + + "dest_link": { + "r2-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link2": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link3": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link4": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link5": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link6": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r2-link7": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + }, + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + + + "dest_link": { + "r3-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link5": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link6": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link7": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + + + "dest_link": { + "r3-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link2": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link3": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link4": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link5": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link6": { + "keepalivetimer": 1, + "holddowntimer": 4 + }, + "r3-link7": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/static_routing_with_ibgp/static_routes_topo3_ibgp.json b/tests/topotests/static_routing_with_ibgp/static_routes_topo3_ibgp.json new file mode 100644 index 0000000000..4e27229f34 --- /dev/null +++ b/tests/topotests/static_routing_with_ibgp/static_routes_topo3_ibgp.json @@ -0,0 +1,189 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 29, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link7": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link0": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link0": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/static_routing_with_ibgp/static_routes_topo4_ibgp.json b/tests/topotests/static_routing_with_ibgp/static_routes_topo4_ibgp.json new file mode 100644 index 0000000000..bb72578ff3 --- /dev/null +++ b/tests/topotests/static_routing_with_ibgp/static_routes_topo4_ibgp.json @@ -0,0 +1,428 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link1": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link2": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link3": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link1": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link2": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r1-link3": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + } + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "password": "r1" + }, + "r2-link1": { + "password": "r1" + }, + "r2-link2": { + "password": "r1" + }, + "r2-link3": { + "password": "r1" + } + } + }, + "r3": { + "dest_link": { + "r2-link0": { + "password": "r1" + }, + "r2-link1": { + "password": "r1" + }, + "r2-link2": { + "password": "r1" + }, + "r2-link3": { + "password": "r1" + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "password": "r1" + }, + "r2-link1": { + "password": "r1" + }, + "r2-link2": { + "password": "r1" + }, + "r2-link3": { + "password": "r1" + } + } + }, + "r3": { + "dest_link": { + "r2-link0": { + "password": "r1" + }, + "r2-link1": { + "password": "r1" + }, + "r2-link2": { + "password": "r1" + }, + "r2-link3": { + "password": "r1" + } + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "vm5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link0": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link1": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link2": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link3": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link0": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link1": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link2": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + }, + "r3-link3": { + "password": "r1", + "holddowntimer": 3, + "keepalivetimer": 1 + } + } + } + } + } + } + } + } + }, + "vm1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "vm2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "vm3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "vm4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "vm5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "vm6": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py new file mode 100644 index 0000000000..130f4fd9aa --- /dev/null +++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py @@ -0,0 +1,1083 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# +""" + + -Verify static route ECMP functionality with 2 next hop + + -Verify static route functionality with 2 next hop and different AD + value + + -Verify RIB status when same route advertise via BGP and static route + +""" +import sys +import json +import time +import os +import pytest +import platform +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_interfaces_cfg, + shutdown_bringup_interface, + stop_router, + start_router, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/static_routes_topo1_ibgp.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": ["11.0.20.1/32", "11.0.20.2/32"], "ipv6": ["2::1/128", "2::2/128"]} +NETWORK2 = {"ipv4": "11.0.20.1/32", "ipv6": "2::1/128"} + +PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"} + + +class CreateTopo(Topo): + """ + Test CreateTopo - topology 1. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + + Set up the pytest environment. + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('These tests will not run. (have kernel "{}", ' + 'requires kernel >= 4.19)'.format(platform.release())) + pytest.skip(error_msg) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def populate_nh(): + NEXT_HOP_IP = { + "nh1": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0], + }, + "nh2": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0], + }, + } + return NEXT_HOP_IP + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_static_route_2nh_p0_tc_1_ibgp(request): + """ + Verify static route ECMP functionality with 2 next hop + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + + step( + "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1" + "(28.1.1.2 ) and N2 (29.1.1.2) , Static route next-hop present on" + "R1" + ) + step("ex :- ip route 10.1.1.1/24 28.1.1.2 & ip route 10.1.1.1/24 29.1.1.1") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + }, + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + }, + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, static route installed in RIB using show ip route" + " with 2 ECMP next hop " + ) + nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("Configure IBGP IPv4 peering between R2 and R3 router.") + step("Configure redistribute static in BGP on R2 router") + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove the static route configured with nexthop N1 from running config") + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, after removing the static route with N1 , " + "route become active with nexthop N2 and vice versa." + ) + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + nh = [NEXT_HOP_IP["nh2"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("Configure the static route with nexthop N1") + + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove the static route configured with nexthop N2 from running config") + + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, after removing the static route with N2 , " + "route become active with nexthop N1 and vice versa." + ) + nh = NEXT_HOP_IP["nh2"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + nh = [NEXT_HOP_IP["nh1"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("Configure the static route with nexthop N2") + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Shut nexthop interface N1") + intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] + + shutdown_bringup_interface(tgen, dut, intf, False) + + step("Only one the nexthops should be active in RIB.") + + nh = NEXT_HOP_IP["nh2"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + dut = "r3" + result = verify_bgp_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + protocol=protocol, + next_hop=nh, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + dut = "r2" + nh = [NEXT_HOP_IP["nh2"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Route is" + " missing in RIB".format(tc_name) + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + dut = "r2" + step("No shut the nexthop interface N1") + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "after shut of nexthop N1 , route become active " + "with nexthop N2 and vice versa." + ) + nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]] + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("Shut nexthop interface N2") + intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] + dut = "r2" + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + " after shut of nexthop N1 , route become active with " + "nexthop N2 and vice versa." + ) + nh = NEXT_HOP_IP["nh2"][addr_type] + + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + nh = [NEXT_HOP_IP["nh1"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Route is" + " missing in RIB".format(tc_name) + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + step("No shut nexthop interface N2") + dut = "r2" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "after shut of nexthop N1 , route become active " + "with nexthop N2 and vice versa." + ) + nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]] + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Route is" + " missing in RIB".format(tc_name) + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + + start_router(tgen, "r2") + + dut = "r2" + step( + "After reload of FRR router , static route installed" + " in RIB and FIB properly ." + ) + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Route is" + " still present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): + """ + Verify static route functionality with 2 next hop & different AD value + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + step( + "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1" + "(28.1.1.2 ) AD 10 and N2 (29.1.1.2) AD 20 , Static route next-hop" + "present on R1 \n ex :- ip route 10.1.1.1/24 28.1.1.2 10 & " + "ip route 10.1.1.1/24 29.1.1.2 20" + ) + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + }, + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + }, + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, static route installed in RIB using " + "show ip route with 2 next hop , lowest AD nexthop is active " + ) + rte1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + nh = [NEXT_HOP_IP["nh1"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "missing in RIB".format(tc_name) + + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + } + ] + } + } + nh = [NEXT_HOP_IP["nh2"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + step("Configure IBGP IPv4 peering between R2 and R3 router.") + step("Explicit route is added in R3 for R2 nexthop rechability") + rt3_rtes = { + "r3": { + "static_routes": [ + { + "network": NEXT_HOP_IP["nh1"][addr_type] + "/32", + "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type], + }, + { + "network": NEXT_HOP_IP["nh2"][addr_type] + "/32", + "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type], + }, + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, rt3_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Configure redistribute static in BGP on R2 router") + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove the static route configured with nexthop N1 from running config") + rt1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, rt1_nh1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, after removing the static route with N1 , " + "route become active with nexthop N2 and vice versa." + ) + rte1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + nh = [NEXT_HOP_IP["nh1"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + rte1_nh1, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "missing in RIB".format(tc_name) + + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + } + ] + } + } + nh = [NEXT_HOP_IP["nh2"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + step("Configure the static route with nexthop N1") + rte1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, rte1_nh1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove the static route configured with nexthop N2 from running config") + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, rte2_nh2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "On R2, after removing the static route with N2 , " + "route become active with nexthop N1 and vice versa." + ) + nh = NEXT_HOP_IP["nh2"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + nh = [NEXT_HOP_IP["nh1"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("Configure the static route with nexthop N2") + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, rte2_nh2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Shut nexthop interface N1") + intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] + + shutdown_bringup_interface(tgen, dut, intf, False) + + step("after shut of nexthop N1 , route become active with nexthop N2") + + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + rte1_nh1, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + nh = [NEXT_HOP_IP["nh2"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("No shut the nexthop interface N1") + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "after shut of nexthop N1 , route become active " + "with nexthop N2 and vice versa." + ) + nh = [NEXT_HOP_IP["nh1"][addr_type]] + + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("Shut nexthop interface N2") + intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] + + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + " after shut of nexthop N1 , route become active with " + "nexthop N2 and vice versa." + ) + nh = NEXT_HOP_IP["nh2"][addr_type] + + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB".format(tc_name) + + nh = [NEXT_HOP_IP["nh1"][addr_type]] + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB".format(tc_name) + + step("No shut nexthop interface N2") + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "after shut of nexthop N1 , route become active " + "with nexthop N2 and vice versa." + ) + rte1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + nh = [NEXT_HOP_IP["nh1"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "missing in RIB".format(tc_name) + + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + } + ] + } + } + nh = [NEXT_HOP_IP["nh2"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + dut = "r3" + protocol = "bgp" + + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + dut = "r2" + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + + start_router(tgen, "r2") + + step( + "After reload of FRR router , static route installed" + " in RIB and FIB properly ." + ) + rte1_nh1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + nh = [NEXT_HOP_IP["nh1"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "missing in RIB".format(tc_name) + + dut = "r3" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "missing in RIB".format(tc_name) + + rte2_nh2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK2[addr_type], + "next_hop": NEXT_HOP_IP["nh2"][addr_type], + "admin_distance": 20, + } + ] + } + } + nh = [NEXT_HOP_IP["nh2"][addr_type]] + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + dut = "r3" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, rte2_nh2, next_hop=nh) + assert result is True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + result = verify_rib( + tgen, + addr_type, + dut, + rte2_nh2, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + "not active in RIB".format(tc_name) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py new file mode 100644 index 0000000000..0a757c9f28 --- /dev/null +++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py @@ -0,0 +1,1974 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + + +""" + -Verify static route functionality with 8 next hop different AD value + and BGP ECMP + + -Verify 8 static route functionality with 8 next hop different AD + + -Verify static route with 8 next hop with different AD value and 8 + EBGP neighbors + + -Verify static route with 8 next hop with different AD value and 8 + IBGP neighbors + + -Delete the static route and verify the RIB and FIB state + + -Verify 8 static route functionality with 8 ECMP next hop +""" +import sys +import json +import time +import os +import pytest +import platform +from time import sleep +import random + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_interfaces_cfg, + shutdown_bringup_interface, + stop_router, + start_router, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.topotest import version_cmp + +# Reading the data from JSON File for topology creation +jsonFile = "{}/static_routes_topo2_ibgp.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + "11.0.20.6/32", + "11.0.20.7/32", + "11.0.20.8/32", + ], + "ipv6": [ + "2::1/128", + "2::2/128", + "2::3/128", + "2::4/128", + "2::5/128", + "2::6/128", + "2::7/128", + "2::8/128", + ], +} +PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"} +PREFIX2 = {"ipv4": "110.0.20.2/32", "ipv6": "20::2/128"} +NEXT_HOP_IP = [] +topo_diag = """ + Please view in a fixed-width font such as Courier. + +------+ +------+ +------+ + | +--------------+ +--------------+ | + | | | | | | + | R1 +---8 links----+ R2 +---8 links----+ R3 | + | | | | | | + | +--------------+ +--------------+ | + +------+ +------+ +------+ + +""" + + +class CreateTopo(Topo): + """ + Test CreateTopo - topology 1. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + + Set up the pytest environment. + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('These tests will not run. (have kernel "{}", ' + 'requires kernel >= 4.19)'.format(platform.release())) + pytest.skip(error_msg) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def populate_nh(): + NEXT_HOP_IP = { + "nh1": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0], + }, + "nh2": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0], + }, + "nh3": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0], + }, + "nh4": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link3"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link3"]["ipv6"].split("/")[0], + }, + "nh5": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link4"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link4"]["ipv6"].split("/")[0], + }, + "nh6": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link5"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link5"]["ipv6"].split("/")[0], + }, + "nh7": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link6"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link6"]["ipv6"].split("/")[0], + }, + "nh8": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link7"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link7"]["ipv6"].split("/")[0], + }, + } + return NEXT_HOP_IP + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_static_rte_with_8ecmp_nh_p1_tc9_ibgp(request): + """ + Verify 8 static route functionality with 8 ECMP next hop + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + NEXT_HOP_IP = populate_nh() + step("Configure 8 interfaces / links between R1 and R2") + step("Configure 8 interfaces / links between R2 and R3") + step("Configure 8 IBGP IPv4 peering between R2 and R3 router.") + reset_config_on_routers(tgen) + + step( + "Configure 8 IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) , N2(22.1.1.2) , N3(23.1.1.2) , N4(24.1.1.2) ," + "N5(25.1.1.2) , N6(26.1.1.2) , N7(27.1.1.2) , N8(28.1.1.2) ," + "Static route next-hop present on R1" + ) + nh_all = {} + for addr_type in ADDR_TYPES: + # Enable static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + nh_all[addr_type] = [ + NEXT_HOP_IP["nh1"][addr_type], + NEXT_HOP_IP["nh2"][addr_type], + NEXT_HOP_IP["nh3"][addr_type], + NEXT_HOP_IP["nh4"][addr_type], + NEXT_HOP_IP["nh5"][addr_type], + NEXT_HOP_IP["nh6"][addr_type], + NEXT_HOP_IP["nh7"][addr_type], + NEXT_HOP_IP["nh8"][addr_type], + ] + + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r3" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + dut = "r2" + protocol = "static" + step( + "After removing the static route with N1 to N8 one by one , " + "verify that entry is removed from RIB and FIB of R3 " + ) + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one , " + "verify that entry is removed from RIB and FIB of R3 " + ) + nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed\nError: Routes is" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by one") + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed\nError: Routes are" + " missing in RIB".format(tc_name) + + protocol = "static" + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + # Shutdown interface + dut = "r2" + step( + " interface which is about to be shut no shut between r1 and r2 is " "%s", + topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"], + ) + intf = topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step("Random no shut of the nexthop interfaces") + # Bringup interface + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "After random shut/no shut of nexthop , only that " + "nexthop deleted/added from all the routes , other nexthop remain " + "unchanged" + ) + dut = "r2" + protocol = "static" + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Remove random static route with all the nexthop") + dut = "r2" + randnum = random.randint(1, 7) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type], + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After delete of random route , that route only got deleted from" + " RIB/FIB other route are showing properly" + ) + nh = NEXT_HOP_IP["nh{}".format(randnum)][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type], + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + start_router(tgen, "r2") + + step( + "After reload of FRR router , static route " + "installed in RIB and FIB properly ." + ) + for addr_type in ADDR_TYPES: + # Enable static routes + nhp = 1 + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + logger.info("Verifying %s routes on r2", addr_type) + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Remove the redistribute static knob") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static", "delete": True} + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the BGP neighbor or redistribute static knob , " + "verify route got clear from RIB and FIB of R3 routes " + ) + dut = "r3" + protocol = "bgp" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ibgp(request): + """ + Verify static route functionality with 8 next hop different AD + value and BGP ECMP + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure 8 interfaces / links between R1 and R2 ,") + step("Configure 8 interlaces/links between R2 and R3") + step( + "Configure IBGP IPv4 peering over loopback interface between" + "R2 and R3 router." + ) + step("Configure redistribute static in BGP on R2 router") + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + nh_all = {} + for addr_type in ADDR_TYPES: + nh_all[addr_type] = [] + for nhp in range(1, 9): + nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30," + "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60," + "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop" + "present on R1" + ) + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + "On R2, static route installed in RIB using " + "show ip route with 8 next hop , lowest AD nexthop is active" + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + wait=2, + attempts=3, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one , " + "route become active with next preferred nexthop and nexthop which " + "got removed is not shown in RIB and FIB" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by one") + + for addr_type in ADDR_TYPES: + # add static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + " After configuring them, route is always active with lowest AD" + " value and all the nexthop populated in RIB and FIB again" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + wait=2, + attempts=3, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + input_dict_5 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_5, + next_hop=nhip, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + + step("Random no shut of the nexthop interfaces") + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + protocol = "bgp" + # this is next hop reachability route in r3 as we are using ibgp + dut = "r3" + for addr_type in ADDR_TYPES: + nh_as_rte = NEXT_HOP_IP["nh1"][addr_type] + "/32" + # add static routes + nh_static_rte = { + "r3": {"static_routes": [{"network": nh_as_rte, "next_hop": "Null0"}]} + } + logger.info("Configure static routes") + result = create_static_routes(tgen, nh_static_rte) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After each interface shut and no shut between R2 -R3 ,verify static" + "route is present in the RIB & FIB of R3 & R2 RIB/FIB is remain" + " unchanged" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + protocol = "static" + dut = "r2" + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {}: Failed \n " "Error: Routes are missing in RIB".format(tc_name) + + protocol = "bgp" + dut = "r3" + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {}: Failed \n " "Error: Routes are missing in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + + start_router(tgen, "r2") + + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, ( + "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + ) + + step("BGP neighbor remove and add") + for rtr in ["r2", "r3"]: + if "bgp" in topo["routers"][rtr].keys(): + delete_bgp = {rtr: {"bgp": {"delete": True}}} + result = create_router_bgp(tgen, topo, delete_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + create_router_bgp(tgen, topo["routers"]) + + NEXT_HOP_IP = populate_nh() + step("Verify routes are still present after delete and add bgp") + dut = "r2" + protocol = "static" + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, ( + "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + ) + + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, ( + "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + ) + + step("Remove the redistribute static knob") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static", "delete": True} + ] + } + } + } + } + } + } + + logger.info("Remove redistribute static") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("verify that routes are deleted from R3 routing table") + + dut = "r3" + protocol = "bgp" + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " strill present in RIB of R3".format(tc_name) + + write_test_footer(tc_name) + + +def test_static_route_8nh_diff_AD_ibgp_ecmp_p1_tc7_ibgp(request): + """ + Verify static route with 8 next hop with different AD value and 8 + IBGP neighbors + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure 8 interfaces / links between R1 and R2") + step("Configure 8 interlaces/links between R2 and R3") + step("Configure 8 IBGP IPv4 peering between R2 and R3") + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30," + "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60," + "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop" + "present on R1" + ) + nh_all = {} + for addr_type in ADDR_TYPES: + nh_all[addr_type] = [] + for nhp in range(1, 9): + nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + "On R2, static route installed in RIB using " + "show ip route with 8 next hop , lowest AD nexthop is active" + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one , " + "route become active with next preferred nexthop and nexthop which " + "got removed is not shown in RIB and FIB" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by one") + + for addr_type in ADDR_TYPES: + # add static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + " After configuring them, route is always active with lowest AD" + " value and all the nexthop populated in RIB and FIB again" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + input_dict_5 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_5, + next_hop=nhip, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + + step("Random no shut of the nexthop interfaces") + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + dut = "r2" + protocol = "bgp" + + # this is next hop reachability route in r3 as we are using ibgp + dut = "r3" + for addr_type in ADDR_TYPES: + nh_as_rte = NEXT_HOP_IP["nh1"][addr_type] + "/32" + # add static routes + nh_static_rte = { + "r3": {"static_routes": [{"network": nh_as_rte, "next_hop": "Null0"}]} + } + logger.info("Configure static routes") + result = create_static_routes(tgen, nh_static_rte) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After each interface shut and no shut between R2 -R3 ,verify static" + "route is present in the RIB & FIB of R3 & R2 RIB/FIB is remain" + " unchanged" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + protocol = "static" + dut = "r2" + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {}: Failed \n " "Error: Routes are missing in RIB".format(tc_name) + + protocol = "bgp" + dut = "r3" + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {}: Failed \n " "Error: Routes are missing in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + + start_router(tgen, "r2") + + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, ( + "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + ) + + step("BGP neighbor remove and add") + for rtr in ["r2", "r3"]: + if "bgp" in topo["routers"][rtr].keys(): + delete_bgp = {rtr: {"bgp": {"delete": True}}} + result = create_router_bgp(tgen, topo, delete_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + create_router_bgp(tgen, topo["routers"]) + + NEXT_HOP_IP = populate_nh() + step("Verify routes are still present after delete and add bgp") + dut = "r2" + protocol = "static" + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, ( + "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + ) + + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, ( + "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + ) + + step("Remove the redistribute static knob") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static", "delete": True} + ] + } + } + } + } + } + } + + logger.info("Remove redistribute static") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("verify that routes are deleted from R3 routing table") + + dut = "r3" + protocol = "bgp" + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB of R3".format(tc_name) + + write_test_footer(tc_name) + + +def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ibgp(request): + """ + Verify 8 static route functionality with 8 next hop different AD' + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + NEXT_HOP_IP = populate_nh() + + step("Configure 8 interfaces / links between R1 and R2 ") + step("Configure 8 IBGP IPv4 peering between R2 and R3 router.") + reset_config_on_routers(tgen) + + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30," + "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60," + "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80" + ) + step( + "Configure nexthop AD in such way for static route S1 , N1 is" + "preferred and for S2 , N2 is preferred and so on .." + ) + nh_all = {} + for addr_type in ADDR_TYPES: + nh_all[addr_type] = [] + for nhp in range(1, 9): + nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + second_rte = { + "r2": { + "static_routes": [ + { + "network": PREFIX2[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, second_rte) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + "On R2, static route installed in RIB using " + "show ip route with 8 next hop , lowest AD nexthop is active" + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Verify that highest AD nexthop are inactive") + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + wait=2, + attempts=3, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one , " + "route become active with next preferred nexthop and nexthop which" + "got removed is not shown in RIB and FIB" + ) + + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by one") + for addr_type in ADDR_TYPES: + # add static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + " After configuring them, route is always active with lowest AD" + " value and all the nexthop populated in RIB and FIB again" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type],}]}} + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route with " + "lowest AD is missing in RIB".format(tc_name) + + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + input_dict_5 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_5, + next_hop=nhip, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + + step("Random no shut of the nexthop interfaces") + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + step("Remove random static route with all the nexthop") + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one , " + "route become active with next preferred nexthop and nexthop " + "which got removed is not shown in RIB and FIB" + ) + nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Route " + " is still present in RIB".format(tc_name) + + step("Reconfigure the deleted routes and verify they are installed") + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, "Testcase {} : Failed \nError: Route " + " is still present in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + + start_router(tgen, "r2") + + step("After reloading, verify that routes are still present in R2.") + result = verify_rib( + tgen, + addr_type, + dut, + second_rte, + next_hop=nh, + protocol=protocol, + fib=True, + ) + assert result is True, ( + "Testcase {} : Failed \nError: Route " + " is missing in RIB".format(tc_name) + ) + + step("Remove the redistribute static knob") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static", "delete": True} + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the BGP neighbor or redistribute static knob , " + "verify route got clear from RIB and FIB of R3 routes " + ) + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_static_route_delete_p0_tc11_ibgp(request): + """ + Delete the static route and verify the RIB and FIB state + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + NEXT_HOP_IP = populate_nh() + + step("Configure 8 interfaces / links between R1 and R2 ") + step("Configure 8 IBGP IPv4 peering between R2 and R3 router.") + reset_config_on_routers(tgen) + + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30," + "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60," + "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80" + ) + step( + "Configure nexthop AD in such way for static route S1 , N1 is" + "preferred and for S2 , N2 is preferred and so on .." + ) + nh_all = {} + for addr_type in ADDR_TYPES: + nh_all[addr_type] = [] + for nhp in range(1, 9): + nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + second_rte = { + "r2": { + "static_routes": [ + { + "network": PREFIX2[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, second_rte) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + "On R2, static route installed in RIB using " + "show ip route with 8 next hop , lowest AD nexthop is active" + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Verify that highest AD nexthop are inactive") + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " are missing in RIB".format(tc_name) + + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove the redistribute static knob") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static", "delete": True} + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify after removing the redistribute static from BGP all the" + "routes got delete from RIB and FIB of R3 " + ) + + dut = "r3" + protocol = "bgp" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + second_rte = { + "r2": { + "static_routes": [ + { + "network": PREFIX2[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, second_rte) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + " After removing all the routes and nexthop from R2 , " + " verify R2 RIB and FIB is cleared" + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still active in RIB".format(tc_name) + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py new file mode 100644 index 0000000000..924fb3a598 --- /dev/null +++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py @@ -0,0 +1,875 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# +""" + -Verify static route ECMP functionality with 8 next hop + + -Verify static route functionality with 8 next hop different AD value + + -Verify static route with tag option + + -Verify BGP did not install the static route when it receive route + with local next hop + +""" +import sys +import json +import time +import os +import pytest +import platform +from copy import deepcopy +import random +from re import search as re_search + + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_interfaces_cfg, + shutdown_bringup_interface, + stop_router, + start_router, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/static_routes_topo3_ibgp.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + "11.0.20.6/32", + "11.0.20.7/32", + "11.0.20.8/32", + ], + "ipv6": [ + "2::1/128", + "2::2/128", + "2::3/128", + "2::4/128", + "2::5/128", + "2::6/128", + "2::7/128", + "2::8/128", + ], +} +PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"} +NETWORK2 = {"ipv4": ["11.0.20.1/32"], "ipv6": ["2::1/128"]} +NEXT_HOP_IP = [] + + +class CreateTopo(Topo): + """ + Test CreateTopo - topology 1. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + + Set up the pytest environment. + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('These tests will not run. (have kernel "{}", ' + 'requires kernel >= 4.19)'.format(platform.release())) + pytest.skip(error_msg) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def populate_nh(): + NEXT_HOP_IP = { + "nh1": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0], + }, + "nh2": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0], + }, + "nh3": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0], + }, + "nh4": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link3"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link3"]["ipv6"].split("/")[0], + }, + "nh5": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link4"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link4"]["ipv6"].split("/")[0], + }, + "nh6": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link5"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link5"]["ipv6"].split("/")[0], + }, + "nh7": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link6"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link6"]["ipv6"].split("/")[0], + }, + "nh8": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link7"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link7"]["ipv6"].split("/")[0], + }, + } + return NEXT_HOP_IP + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_staticroute_with_ecmp_p0_tc3_ibgp(request): + """ + Verify static route ECMP functionality with 8 next hop' + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + + step("Configure 8 interfaces / links between R1 and R2,") + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2), N2(22.1.1.2), N3(23.1.1.2), N4(24.1.1.2)," + "N5(25.1.1.2), N6(26.1.1.2), N7(27.1.1.2),N8(28.1.1.2), Static" + "route next-hop present on R1" + ) + + step("Configure IBGP IPv4 peering between R2 and R3 router.") + + for addr_type in ADDR_TYPES: + # Enable static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + nh = [ + NEXT_HOP_IP["nh1"][addr_type], + NEXT_HOP_IP["nh2"][addr_type], + NEXT_HOP_IP["nh3"][addr_type], + NEXT_HOP_IP["nh4"][addr_type], + NEXT_HOP_IP["nh5"][addr_type], + NEXT_HOP_IP["nh6"][addr_type], + NEXT_HOP_IP["nh7"][addr_type], + NEXT_HOP_IP["nh8"][addr_type], + ] + + dut = "r2" + protocol = "static" + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by" "one") + + for addr_type in ADDR_TYPES: + # add static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + input_dict_5 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_5, + next_hop=nhip, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + + step("Random no shut of the nexthop interfaces") + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + start_router(tgen, "r2") + + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \nError: Routes are" + " missing in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ibgp(request): + """ + Verify static route ECMP functionality with 8 next hop + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + + step("Configure 8 interfaces / links between R1 and R2,") + step("Configure IBGP IPv4 peering between R2 and R3 router.") + reset_config_on_routers(tgen) + NEXT_HOP_IP = populate_nh() + nh_all = {} + for addr_type in ADDR_TYPES: + nh_all[addr_type] = [] + for nhp in range(1, 9): + nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + step( + "Configure IPv4 static route in R2 with 8 next hop" + "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30," + "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60," + "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop" + "present on R1" + ) + for addr_type in ADDR_TYPES: + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + logger.info("Verifying %s routes on r2", addr_type) + + step( + "On R2, static route installed in RIB using " + "show ip route with 8 next hop, lowest AD nexthop is active" + ) + step("On R2, static route with lowest AD nexthop installed in FIB") + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route with " + " lowest AD is missing in RIB".format(tc_name) + + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " with high AD are active in RIB".format(tc_name) + + step("Configure redistribute static in BGP on R2 router") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + + logger.info("Configuring redistribute static") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After configuring them, route is always active with lowest AD" + "value and all the nexthop populated in RIB and FIB again " + ) + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route with " + " lowest AD is missing in RIB".format(tc_name) + + step( + "Remove the static route configured with nexthop N1 to N8, one" + "by one from running config" + ) + + for addr_type in ADDR_TYPES: + # delete static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + "delete": True, + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing the static route with N1 to N8 one by one, " + "route become active with next preferred nexthop and nexthop which " + "got removed is not shown in RIB and FIB" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh_all[addr_type], + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " still present in RIB".format(tc_name) + + step("Configure the static route with nexthop N1 to N8, one by" "one") + for addr_type in ADDR_TYPES: + # add static routes + for nhp in range(1, 9): + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type], + "admin_distance": 10 * nhp, + } + ] + } + } + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("On R2, static route with lowest AD nexthop installed in FIB") + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route with " + " lowest AD is missing in RIB".format(tc_name) + + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " with high AD are active in RIB".format(tc_name) + + step("Random shut of the nexthop interfaces") + randnum = random.randint(0, 7) + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + input_dict_5 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type], + } + ] + } + } + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_5, + next_hop=nhip, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Error: Routes are still present in RIB".format(tc_name) + + step("Random no shut of the nexthop interfaces") + for addr_type in ADDR_TYPES: + intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n" + "Error: Routes are missing in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r2") + start_router(tgen, "r2") + + step( + "After reload of FRR router, static route installed " + "in RIB and FIB properly ." + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": PREFIX1[addr_type], + "next_hop": NEXT_HOP_IP["nh1"][addr_type], + "admin_distance": 10, + } + ] + } + } + dut = "r2" + protocol = "static" + nh = NEXT_HOP_IP["nh1"][addr_type] + result = verify_rib( + tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True + ) + assert result is True, "Testcase {} : Failed \nError: Route with " + " lowest AD is missing in RIB".format(tc_name) + + nh = [] + for nhp in range(2, 9): + nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + fib=True, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes " + " with high AD are active in RIB".format(tc_name) + + step("Remove the redistribute static knob") + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static", "delete": True} + ] + } + } + } + } + } + } + + logger.info("Remove redistribute static") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("verify that routes are deleted from R3 routing table") + dut = "r3" + protocol = "bgp" + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=nh, + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \nError: Routes are" + " strill present in RIB of R3".format(tc_name) + + write_test_footer(tc_name) + + +def test_bgp_local_nexthop_p1_tc14_ibgp(request): + """ + Verify BGP did not install the static route when it receive route + with local next hop + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + step("Configure BGP IPv4 session between R2 and R3") + step("Configure IPv4 static route on R2") + reset_config_on_routers(tgen) + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r3"]["links"]["r2-link0"][ + addr_type + ].split("/")[0], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure redistribute static in the BGP") + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify R2 BGP table has IPv4 route") + dut = "r2" + result = verify_rib(tgen, addr_type, dut, input_dict_4) + assert result is True, "Testcase {} : Failed \nError: Routes is" + " missing in RIB of R2".format(tc_name) + + step(" Verify route did not install in the R3 BGP table, RIB/FIB") + dut = "r3" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4, expected=False) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in BGP RIB of R2".format(tc_name) + + result = verify_rib(tgen, addr_type, dut, input_dict_4, expected=False) + assert result is not True, "Testcase {} : Failed \nError: Routes is" + " still present in RIB of R2".format(tc_name) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py new file mode 100644 index 0000000000..fdbfad25b3 --- /dev/null +++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py @@ -0,0 +1,991 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +""" + +Following tests are covered in the script. + +- Verify static route are blocked from route-map and prefix-list + applied in BGP nbrs +""" + +import sys +import json +import time +import os +import pytest +import platform +import ipaddress +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + check_address_types, + step, + create_prefix_lists, + create_route_maps, + create_interfaces_cfg, + verify_prefix_lists, + verify_route_maps, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp_and_verify, + clear_bgp, +) +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.topotest import version_cmp +# Reading the data from JSON File for topology creation +jsonFile = "{}/static_routes_topo4_ibgp.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"} +NEXT_HOP_IP = {} + + +class CreateTopo(Topo): + """ + Test CreateTopo - topology 1. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Set up the pytest environment. + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('These tests will not run. (have kernel "{}", ' + 'requires kernel >= 4.19)'.format(platform.release())) + pytest.skip(error_msg) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### +def test_static_routes_rmap_pfxlist_p0_tc7_ibgp(request): + """ + Verify static route are blocked from route-map & prefix-list applied in BGP + nbrs + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step("Configure holddown timer = 1 keep alive = 3 in all the neighbors") + step("verify bgp convergence before starting test case") + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step( + "Configure 4 IPv4 and 4 IPv6 nbrs with password with mismatch " + " authentication between FRR routers " + ) + + for addr_type in ADDR_TYPES: + # Api call to modfiy BGP timerse + input_dict = { + "r2": { + "bgp": { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": {"password": "r2"}, + "r2-link1": {"password": "r2"}, + "r2-link2": {"password": "r2"}, + "r2-link3": {"password": "r2"}, + } + }, + "r3": { + "dest_link": { + "r2-link0": {"password": "r2"}, + "r2-link1": {"password": "r2"}, + "r2-link2": {"password": "r2"}, + "r2-link3": {"password": "r2"}, + } + }, + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, deepcopy(input_dict)) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + clear_bgp(tgen, addr_type, "r2") + + step(" All BGP nbrs are down as authentication is mismatch on both" " the sides") + + bgp_convergence = verify_bgp_convergence(tgen, topo, expected=False) + assert bgp_convergence is not True, "Testcase {} : " + "Failed \n BGP nbrs must be down. Error: {}".format(tc_name, bgp_convergence) + + step( + "Configure 4 IPv4 and 4 IPv6 nbrs with macthing password " + " authentication between FRR routers " + ) + for addr_type in ADDR_TYPES: + input_dict = { + "r2": { + "bgp": { + "local_as": "200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": {"password": "r1"}, + "r2-link1": {"password": "r1"}, + "r2-link2": {"password": "r1"}, + "r2-link3": {"password": "r1"}, + } + }, + "r3": { + "dest_link": { + "r2-link0": {"password": "r1"}, + "r2-link1": {"password": "r1"}, + "r2-link2": {"password": "r1"}, + "r2-link3": {"password": "r1"}, + } + }, + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, deepcopy(input_dict)) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("All BGP nbrs are up as authentication is matched now") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} : Failed \n " "Error: {}".format( + tc_name, bgp_convergence + ) + + step("Create prefix list P1 to permit VM3 & deny VM1 v4 & v6 routes") + step("Create prefix list P2 to permit VM6 IPv4 and IPv6 routes") + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "prefix_lists": { + addr_type: { + "pf_list_1_{}".format(addr_type): [ + { + "seqid": 10, + "network": topo["routers"]["r2"]["links"]["vm3"][ + addr_type + ], + "action": "permit", + }, + { + "seqid": 20, + "network": topo["routers"]["r2"]["links"]["vm1"][ + addr_type + ], + "action": "deny", + }, + ], + "pf_list_2_{}".format(addr_type): [ + { + "seqid": 10, + "network": topo["routers"]["r2"]["links"]["vm6"][ + addr_type + ], + "action": "permit", + } + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Prefix list created with matching networks deny or permit " + "show ip prefix list" + ) + result = verify_prefix_lists(tgen, input_dict_2) + assert result is not True, "Testcase {} : Failed \n" + " Error: {}".format(tc_name, result) + + step("Redistribute all the routes (connected, static)") + input_dict_2_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2_r2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2_r3 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute connected in Router BGP") + + input_dict_2_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2_r3 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply prefix list P1 on BGP neighbors 1 2 3 4 connected from " "frr r1") + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link1": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link2": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link3": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply prefix list P2 on BGP nbrs 5 & 6 connected from FRR-2") + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link1": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link2": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + "r2-link3": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + clear_bgp_and_verify(tgen, topo, "r2") + + step( + "VM1 IPv4 and IPv6 Route which is denied using prefix list is " + "not present on FRR1 side routing table , also not able to " + "ping the routes show ip route" + ) + + dut = "r1" + protocol = "bgp" + ntwk_r2_vm1 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r2"]["links"]["vm1"][addr_type]) + ).network + ) + input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}} + result4 = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result4 is not True, "Testcase {} : Failed , VM1 route is " + "not filtered out via prefix list. \n Error: {}".format(tc_name, result4) + + step( + "VM4 and VM6 IPV4 and IPv6 address are present in local and " + "FRR2 routing table show ip bgp show ip route" + ) + + dut = "r2" + ntwk_r2_vm6 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r2"]["links"]["vm6"][addr_type]) + ).network + ) + input_dict = {"r3": {"static_routes": [{"network": ntwk_r2_vm6}]}} + result4 = verify_rib(tgen, addr_type, dut, input_dict) + assert result4 is True, "Testcase {} : Failed.\n Error: {}".format( + tc_name, result4 + ) + + step("Remove prefix list from all the neighbors") + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link1": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link2": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link3": { + "prefix_lists": [ + { + "name": "pf_list_1_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link1": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link2": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + "r2-link3": { + "prefix_lists": [ + { + "name": "pf_list_2_{}".format( + addr_type + ), + "direction": "out", + "delete": True, + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + clear_bgp_and_verify(tgen, topo, "r2") + + step("Create RouteMap_1 with prefix list P1 and weight 50") + # Create route map + rmap_dict = { + "r2": { + "route_maps": { + "rmap_pf_list_1_{}".format(addr_type): [ + { + "action": "permit", + "set": {"weight": 50}, + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, rmap_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Create RouteMap_2 with prefix list P2 and weight 50") + # Create route map + rmap_dict = { + "r2": { + "route_maps": { + "rmap_pf_list_2_{}".format(addr_type): [ + { + "action": "permit", + "set": {"weight": 50}, + "match": { + addr_type: { + "prefix_lists": "pf_list_2_{}".format(addr_type) + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, rmap_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify Route-map created verify using show route-map") + # verify rmap_pf_list_1 and rmap_pf_list_2 are present in router r2 + input_dict = { + "r2": { + "route_maps": [ + "rmap_pf_list_1_{}".format(addr_type), + "rmap_pf_list_2_{}".format(addr_type), + ] + } + } + result = verify_route_maps(tgen, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply policy RouteMap_1 nbrs 1 2 3 4 to FRR 1") + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "route_maps": [ + { + "name": "rmap_pf_list_1_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link1": { + "route_maps": [ + { + "name": "rmap_pf_list_1_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link2": { + "route_maps": [ + { + "name": "rmap_pf_list_1_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link3": { + "route_maps": [ + { + "name": "rmap_pf_list_1_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply policy RouteMap_2 nbrs 5 and 6 to FRR2") + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link0": { + "route_maps": [ + { + "name": "rmap_pf_list_2_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link1": { + "route_maps": [ + { + "name": "rmap_pf_list_2_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link2": { + "route_maps": [ + { + "name": "rmap_pf_list_2_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + "r2-link3": { + "route_maps": [ + { + "name": "rmap_pf_list_2_" + "{}".format(addr_type), + "direction": "out", + } + ] + }, + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After applying to BGP neighbors verify VM1 IPv4 and IPv6 Route" + " which is denied using prefix list is not present on FRR side" + " routing table , also not able to ping the routes show ip route" + " and VM4 and VM6 IPV4 and IPv6 address are present in local and" + " FRR routing table show ip bgp show ip route" + ) + + dut = "r1" + protocol = "bgp" + ntwk_r2_vm1 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r2"]["links"]["vm1"][addr_type]) + ).network + ) + input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}} + result4 = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result4 is not True, "Testcase {} : Failed \n" "Error: {}".format( + tc_name, result4 + ) + + step("vm4 should be present in FRR1") + dut = "r1" + ntwk_r2_vm1 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r1"]["links"]["vm4"][addr_type]) + ).network + ) + input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}} + result4 = verify_rib(tgen, addr_type, dut, input_dict) + assert result4 is True, "Testcase {} : Failed , VM1 route is " + "not filtered out via prefix list. \n Error: {}".format(tc_name, result4) + + step("vm4 should be present in FRR2") + dut = "r2" + ntwk_r2_vm1 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r1"]["links"]["vm4"][addr_type]) + ).network + ) + input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}} + result4 = verify_rib(tgen, addr_type, dut, input_dict) + assert result4 is True, "Testcase {} : Failed , VM1 route is " + "not filtered out via prefix list. \n Error: {}".format(tc_name, result4) + + dut = "r3" + protocol = "bgp" + ntwk_r2_vm6 = str( + ipaddress.ip_interface( + u"{}".format(topo["routers"]["r2"]["links"]["vm6"][addr_type]) + ).network + ) + input_dict = {"r3": {"static_routes": [{"network": ntwk_r2_vm6}]}} + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed.\n Error: {}".format( + tc_name, result4 + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py index 4da5303641..94baf8438f 100644 --- a/tests/topotests/zebra_netlink/test_zebra_netlink.py +++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py @@ -118,7 +118,12 @@ def test_zebra_netlink_batching(): r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 100") json_file = "{}/r1/v4_route.json".format(CWD) expected = json.loads(open(json_file).read()) - test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expected,) + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip route json", + expected, + ) _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) assertmsg = '"r1" JSON output mismatches' assert result is None, assertmsg diff --git a/tests/topotests/zebra_rib/r1/iproute.ref b/tests/topotests/zebra_rib/r1/iproute.ref new file mode 100644 index 0000000000..b28182c2d1 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/iproute.ref @@ -0,0 +1,512 @@ +4.5.1.0/24 via 192.168.210.2 dev r1-eth0 metric 4278198272 +4.5.2.0/24 via 192.168.211.2 dev r1-eth1 metric 16777217 +4.5.3.0/24 via 192.168.212.2 dev r1-eth2 metric 167772161 +4.5.3.0/24 via 192.168.213.2 dev r1-eth3 metric 2684354561 +10.0.0.0 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.1 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.2 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.3 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.4 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.5 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.6 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.7 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.8 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.9 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.10 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.11 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.12 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.13 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.14 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.15 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.16 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.17 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.18 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.19 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.20 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.21 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.22 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.23 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.24 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.25 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.26 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.27 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.28 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.29 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.30 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.31 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.32 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.33 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.34 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.35 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.36 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.37 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.38 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.39 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.40 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.41 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.42 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.43 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.45 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.46 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.47 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.48 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.49 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.50 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.51 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.52 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.53 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.54 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.55 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.56 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.57 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.58 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.59 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.60 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.61 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.62 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.63 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.64 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.65 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.66 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.67 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.68 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.69 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.70 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.71 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.72 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.73 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.74 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.75 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.76 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.77 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.78 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.79 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.80 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.81 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.82 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.83 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.84 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.85 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.86 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.87 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.88 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.89 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.90 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.91 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.92 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.93 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.94 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.95 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.96 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.97 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.98 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.99 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.100 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.101 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.102 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.103 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.104 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.105 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.106 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.107 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.108 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.109 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.110 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.111 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.112 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.113 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.114 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.115 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.116 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.117 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.118 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.119 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.120 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.121 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.122 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.123 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.124 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.125 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.126 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.127 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.128 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.129 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.130 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.131 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.132 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.133 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.134 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.135 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.136 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.137 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.138 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.139 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.140 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.141 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.142 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.143 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.144 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.145 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.146 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.147 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.148 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.149 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.150 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.151 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.152 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.153 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.154 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.155 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.156 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.157 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.158 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.159 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.160 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.161 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.162 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.163 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.164 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.165 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.166 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.167 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.168 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.169 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.170 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.171 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.172 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.173 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.174 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.175 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.176 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.177 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.178 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.179 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.180 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.181 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.182 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.183 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.184 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.185 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.186 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.187 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.188 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.189 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.190 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.191 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.192 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.193 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.194 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.195 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.196 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.197 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.198 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.199 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.200 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.201 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.202 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.203 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.204 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.205 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.206 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.207 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.208 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.209 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.210 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.211 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.212 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.213 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.214 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.215 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.216 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.217 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.218 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.219 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.220 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.221 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.222 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.223 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.224 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.225 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.226 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.227 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.228 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.229 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.230 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.231 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.232 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.233 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.234 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.235 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.236 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.237 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.238 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.239 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.240 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.241 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.242 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.243 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.244 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.245 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.246 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.247 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.248 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.249 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.250 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.251 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.252 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.253 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.254 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.0.255 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20 +10.0.1.0 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.1 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.2 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.3 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.4 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.5 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.6 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.7 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.8 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.9 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.10 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.11 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.12 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.13 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.14 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.15 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.16 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.17 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.18 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.19 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.20 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.21 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.22 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.23 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.24 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.25 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.26 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.27 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.28 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.29 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.30 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.31 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.32 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.33 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.34 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.35 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.36 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.37 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.38 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.39 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.40 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.41 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.42 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.43 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.44 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.45 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.46 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.47 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.48 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.49 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.50 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.51 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.52 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.53 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.54 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.55 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.56 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.57 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.58 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.59 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.60 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.61 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.62 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.63 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.64 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.65 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.66 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.67 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.68 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.69 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.70 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.71 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.72 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.73 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.74 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.75 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.76 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.77 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.78 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.79 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.80 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.81 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.82 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.83 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.84 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.85 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.86 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.87 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.88 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.89 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.90 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.91 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.92 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.93 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.94 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.95 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.96 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.97 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.98 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.99 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.100 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.101 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.102 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.103 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.104 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.105 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.106 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.107 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.108 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.109 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.110 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.111 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.112 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.113 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.114 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.115 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.116 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.117 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.118 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.119 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.120 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.121 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.122 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.123 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.124 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.125 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.126 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.127 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.128 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.129 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.130 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.131 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.132 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.133 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.134 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.135 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.136 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.137 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.138 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.139 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.140 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.141 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.142 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.143 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.144 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.145 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.146 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.147 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.148 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.149 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.150 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.151 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.152 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.153 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.154 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.155 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.156 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.157 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.158 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.159 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.160 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.161 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.162 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.163 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.164 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.165 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.166 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.167 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.168 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.169 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.170 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.171 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.172 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.173 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.174 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.175 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.176 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.177 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.178 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.179 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.180 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.181 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.182 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.183 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.184 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.185 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.186 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.187 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.188 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.189 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.190 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.191 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.192 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.193 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.194 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.195 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.196 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.197 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.198 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.199 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.200 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.201 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.202 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.203 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.204 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.205 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.206 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.207 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.208 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.209 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.210 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.211 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.212 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.213 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.214 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.215 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.216 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.217 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.218 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.219 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.220 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.221 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.222 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.223 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.224 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.225 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.226 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.227 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.228 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.229 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.230 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.231 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.232 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.233 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.234 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.235 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.236 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.237 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.238 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.239 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.240 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.241 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.242 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.0.1.243 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20 +10.100.100.100 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.215.1 metric 20 +192.168.210.0/24 dev r1-eth0 proto XXXX scope link src 192.168.210.1 +192.168.211.0/24 dev r1-eth1 proto XXXX scope link src 192.168.211.1 +192.168.212.0/24 dev r1-eth2 proto XXXX scope link src 192.168.212.1 +192.168.213.0/24 dev r1-eth3 proto XXXX scope link src 192.168.213.1 +192.168.214.0/24 dev r1-eth4 proto XXXX scope link src 192.168.214.1 +192.168.215.0/24 dev r1-eth5 proto XXXX scope link src 192.168.215.1 +192.168.216.0/24 dev r1-eth6 proto XXXX scope link src 192.168.216.1 +192.168.217.0/24 dev r1-eth7 proto XXXX scope link src 192.168.217.1 diff --git a/tests/topotests/zebra_rib/r1/sharp_rmap.ref b/tests/topotests/zebra_rib/r1/sharp_rmap.ref new file mode 100644 index 0000000000..47a9eb6a49 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/sharp_rmap.ref @@ -0,0 +1,17 @@ +ZEBRA: +route-map: sharp Invoked: 500 Optimization: enabled Processed Change: false + permit, sequence 10 Invoked 244 + Match clauses: + ip address 10 + Set clauses: + src 192.168.214.1 + Call clause: + Action: + Exit routemap + permit, sequence 20 Invoked 256 + Match clauses: + Set clauses: + src 192.168.213.1 + Call clause: + Action: + Exit routemap diff --git a/tests/topotests/zebra_rib/r1/static_rmap.ref b/tests/topotests/zebra_rib/r1/static_rmap.ref new file mode 100644 index 0000000000..2de98bd514 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/static_rmap.ref @@ -0,0 +1,9 @@ +ZEBRA: +route-map: static Invoked: 2 Optimization: enabled Processed Change: false + permit, sequence 10 Invoked 2 + Match clauses: + Set clauses: + src 192.168.215.1 + Call clause: + Action: + Exit routemap diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index ef4b597206..daf8f7be20 100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -41,6 +41,7 @@ sys.path.append(os.path.join(CWD, "../")) from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger +from time import sleep # Required to instantiate the topology builder class. from mininet.topo import Topo @@ -75,8 +76,9 @@ def setup_module(mod): router_list = tgen.routers() for rname, router in router_list.items(): router.load_config( - TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) - ) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))) + router.load_config( + TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))) # Initialize all routers. tgen.start_router() @@ -157,6 +159,111 @@ def test_zebra_kernel_override(): _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) assert result is None, '"r1" JSON output mismatches' +def test_route_map_usage(): + "Test that FRR only reruns over routes associated with the routemap" + logger.info("Test that FRR runs on selected re's on route-map changes") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("Skipped because of previous test failure") + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + r1 = tgen.gears["r1"] + # set the delay timer to 1 to improve test coverage (HA) + r1.vtysh_cmd("conf\nzebra route-map delay-timer 1") + r1.vtysh_cmd("conf\nroute-map static permit 10\nset src 192.168.215.1") + r1.vtysh_cmd("conf\naccess-list 5 seq 5 permit 10.0.0.44/32") + r1.vtysh_cmd("conf\naccess-list 10 seq 5 permit 10.0.1.0/24") + r1.vtysh_cmd("conf\nroute-map sharp permit 10\nmatch ip address 10\nset src 192.168.214.1") + r1.vtysh_cmd("conf\nroute-map sharp permit 20\nset src 192.168.213.1") + r1.vtysh_cmd("conf\nip protocol static route-map static") + r1.vtysh_cmd("conf\nip protocol sharp route-map sharp") + sleep(4) + r1.vtysh_cmd("conf\nip route 10.100.100.100/32 192.168.216.3") + r1.vtysh_cmd("conf\nip route 10.100.100.101/32 10.0.0.44") + r1.vtysh_cmd("sharp install route 10.0.0.0 nexthop 192.168.216.3 500") + sleep(4) + + static_rmapfile = "%s/r1/static_rmap.ref" % (thisDir) + expected = open(static_rmapfile).read().rstrip() + expected = ('\n'.join(expected.splitlines()) + '\n').rstrip() + actual = r1.vtysh_cmd("show route-map static") + actual = ('\n'.join(actual.splitlines()) + '\n').rstrip() + logger.info("Does the show route-map static command run the correct number of times") + + diff = topotest.get_textdiff(actual, expected, + title1 = "Actual Route-map output", + title2 = "Expected Route-map output") + if diff: + logger.info("Actual:") + logger.info(actual) + logger.info("Expected:") + logger.info(expected) + srun = r1.vtysh_cmd("show run") + srun = ('\n'.join(srun.splitlines()) + '\n').rstrip() + logger.info("Show run") + logger.info(srun) + assert 0, "r1 static route processing:\n" + + sharp_rmapfile = "%s/r1/sharp_rmap.ref" % (thisDir) + expected = open(sharp_rmapfile).read().rstrip() + expected = ('\n'.join(expected.splitlines()) + '\n').rstrip() + actual = r1.vtysh_cmd("show route-map sharp") + actual = ('\n'.join(actual.splitlines()) + '\n').rstrip() + logger.info("Does the show route-map sharp command run the correct number of times") + + diff = topotest.get_textdiff(actual, expected, + title1 = "Actual Route-map output", + title2 = "Expected Route-map output") + if diff: + logger.info("Actual:") + logger.info(actual) + logger.info("Expected:") + logger.info(expected) + srun = r1.vtysh_cmd("show run") + srun = ('\n'.join(srun.splitlines()) + '\n').rstrip() + logger.info("Show run:") + logger.info(srun) + assert 0, "r1 sharp route-map processing:\n" + + logger.info("Add a extension to the static route-map to see the static route go away") + r1.vtysh_cmd("conf\nroute-map sharp deny 5\nmatch ip address 5") + sleep(2) + # we are only checking the kernel here as that this will give us the implied + # testing of both the route-map and staticd withdrawing the route + # let's spot check that the routes were installed correctly + # in the kernel + logger.info("Test that the routes installed are correct") + sharp_ipfile = "%s/r1/iproute.ref" % (thisDir) + expected = open(sharp_ipfile).read().rstrip() + expected = ('\n'.join(expected.splitlines()) + '\n').rstrip() + actual = r1.run("ip route show") + actual = ('\n'.join(actual.splitlines()) + '\n').rstrip() + actual = re.sub(r" nhid [0-9][0-9]", "", actual) + actual = re.sub(r" proto sharp", " proto XXXX", actual) + actual = re.sub(r" proto static", " proto XXXX", actual) + actual = re.sub(r" proto 194", " proto XXXX", actual) + actual = re.sub(r" proto 196", " proto XXXX", actual) + actual = re.sub(r" proto kernel", " proto XXXX", actual) + actual = re.sub(r" proto 2", " proto XXXX", actual) + # Some platforms have double spaces? Why?????? + actual = re.sub(r" proto XXXX ", " proto XXXX ", actual) + actual = re.sub(r" metric", " metric", actual) + actual = re.sub(r" link ", " link ", actual) + diff = topotest.get_textdiff(actual, expected, + title1 = "Actual ip route show", + title2 = "Expected ip route show") + + if diff: + logger.info("Actual:") + logger.info(actual) + logger.info("Expected:") + logger.info(expected) + srun = r1.vtysh_cmd("show run") + srun = ('\n'.join(srun.splitlines()) + '\n').rstrip() + logger.info("Show run:") + logger.info(srun) + assert 0, "r1 ip route show is not correct:" def test_memory_leak(): "Run the memory leak test and report results." diff --git a/tests/zebra/test_lm_plugin.c b/tests/zebra/test_lm_plugin.c new file mode 100644 index 0000000000..4a9344fee4 --- /dev/null +++ b/tests/zebra/test_lm_plugin.c @@ -0,0 +1,134 @@ +/* + * Label Manager tests. + * Copyright (C) 2020 Volta Networks + * Patrick Ruddy + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include <zebra.h> +#include "zebra/zapi_msg.h" +#include "zebra/label_manager.h" + +/* shim out unused functions/variables to allow the lablemanager to compile*/ +DEFINE_KOOH(zserv_client_close, (struct zserv * client), (client)); +unsigned long zebra_debug_packet = 0; +struct zserv *zserv_find_client_session(uint8_t proto, unsigned short instance, + uint32_t session_id) +{ + return NULL; +} + +int zsend_label_manager_connect_response(struct zserv *client, vrf_id_t vrf_id, + unsigned short result) +{ + return 0; +} + +int zsend_assign_label_chunk_response(struct zserv *client, vrf_id_t vrf_id, + struct label_manager_chunk *lmc) +{ + return 0; +} + + +static int test_client_connect(struct zserv *client, vrf_id_t vrf_id) +{ + return 0; +} + +static int test_client_disconnect(struct zserv *client) +{ + return 0; +} + +/* external test hook functions */ +static int lm_get_chunk_pi(struct label_manager_chunk **lmc, + struct zserv *client, uint8_t keep, uint32_t size, + uint32_t base, vrf_id_t vrf_id) +{ + if (base == 0) + *lmc = create_label_chunk(10, 55, 0, 1, 50, 50 + size); + else + *lmc = assign_label_chunk(10, 55, 0, 1, size, base); + + return 0; +} + +static int lm_release_chunk_pi(struct zserv *client, uint32_t start, + uint32_t end) +{ + return release_label_chunk(client->proto, client->instance, + client->session_id, start, end); +} + + +/* use external allocations */ +static void lp_plugin_init() +{ + /* register our own hooks */ + hook_register(lm_client_connect, test_client_connect); + hook_register(lm_client_disconnect, test_client_disconnect); + hook_register(lm_get_chunk, lm_get_chunk_pi); + hook_register(lm_release_chunk, lm_release_chunk_pi); +} + +static void lp_plugin_cleanup() +{ + /* register our own hooks */ + hook_unregister(lm_client_connect, test_client_connect); + hook_unregister(lm_client_disconnect, test_client_disconnect); + hook_unregister(lm_get_chunk, lm_get_chunk_pi); + hook_unregister(lm_release_chunk, lm_release_chunk_pi); +} + + +/* tests */ + +static void test_lp_plugin() +{ + struct label_manager_chunk *lmc; + + lmc = assign_label_chunk(10, 55, 0, 1, 50, 0); + fprintf(stdout, + "chunk: start %u end %u proto %u instance %u session %u keep %s\n", + lmc->start, lmc->end, lmc->proto, lmc->instance, + lmc->session_id, lmc->keep ? "yes" : "no"); + delete_label_chunk(lmc); + + lmc = assign_label_chunk(10, 55, 0, 1, 50, 100); + fprintf(stdout, + "chunk: start %u end %u proto %u instance %u session %u keep %s\n", + lmc->start, lmc->end, lmc->proto, lmc->instance, + lmc->session_id, lmc->keep ? "yes" : "no"); + release_label_chunk(10, 55, 0, lmc->start, lmc->end); +} + +int main(int argc, char **argv) +{ + /* set up label manager and release it's hooks */ + label_manager_init(); + lm_hooks_unregister(); + + /* test plugin */ + lp_plugin_init(); + test_lp_plugin(); + lp_plugin_cleanup(); + + /* this keeps the compiler happy */ + hook_call(zserv_client_close, NULL); + return 0; +} diff --git a/tests/zebra/test_lm_plugin.py b/tests/zebra/test_lm_plugin.py new file mode 100644 index 0000000000..bf4f3cef91 --- /dev/null +++ b/tests/zebra/test_lm_plugin.py @@ -0,0 +1,5 @@ +import frrtest + + +class TestLmplugin(frrtest.TestRefOut): + program = "./test_lm_plugin" diff --git a/tests/zebra/test_lm_plugin.refout b/tests/zebra/test_lm_plugin.refout new file mode 100644 index 0000000000..35824f1055 --- /dev/null +++ b/tests/zebra/test_lm_plugin.refout @@ -0,0 +1,2 @@ +chunk: start 16 end 65 proto 10 instance 55 session 0 keep yes +chunk: start 100 end 149 proto 10 instance 55 session 0 keep yes |
