diff options
Diffstat (limited to 'tests')
452 files changed, 53346 insertions, 3121 deletions
diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index 54596dbdfb..ae7903e0cc 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -1042,9 +1042,9 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) if (!parse_ret) { if (type == BGP_ATTR_MP_REACH_NLRI) - nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 0); + nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, false); else if (type == BGP_ATTR_MP_UNREACH_NLRI) - nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 1); + nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, true); } handle_result(peer, t, parse_ret, nlri_ret); } diff --git a/tests/isisd/test_common.c b/tests/isisd/test_common.c index d0288f600d..e47456965e 100644 --- a/tests/isisd/test_common.c +++ b/tests/isisd/test_common.c @@ -98,7 +98,7 @@ static void lsp_add_ip_reach(struct isis_lsp *lsp, { struct prefix prefix; struct sr_prefix_cfg pcfg = {}; - struct sr_prefix_cfg *pcfg_p = NULL; + struct sr_prefix_cfg *pcfg_p[SR_ALGORITHM_COUNT] = {NULL}; if (str2prefix(prefix_str, &prefix) != 1) { zlog_debug("%s: invalid network: %s", __func__, prefix_str); @@ -106,7 +106,7 @@ static void lsp_add_ip_reach(struct isis_lsp *lsp, } if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) { - pcfg_p = &pcfg; + pcfg_p[SR_ALGORITHM_SPF] = &pcfg; pcfg.sid = *next_sid_index; *next_sid_index = *next_sid_index + 1; @@ -163,31 +163,32 @@ static void lsp_add_reach(struct isis_lsp *lsp, static void lsp_add_router_capability(struct isis_lsp *lsp, const struct isis_test_node *tnode) { - struct isis_router_cap cap = {}; + struct isis_router_cap *cap; if (!tnode->router_id) return; - if (inet_pton(AF_INET, tnode->router_id, &cap.router_id) != 1) { + cap = isis_tlvs_init_router_capability(lsp->tlvs); + + if (inet_pton(AF_INET, tnode->router_id, &cap->router_id) != 1) { zlog_debug("%s: invalid router-id: %s", __func__, tnode->router_id); return; } if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) { - cap.srgb.flags = + cap->srgb.flags = ISIS_SUBTLV_SRGB_FLAG_I | ISIS_SUBTLV_SRGB_FLAG_V; - cap.srgb.lower_bound = tnode->srgb.lower_bound - ? tnode->srgb.lower_bound - : SRGB_DFTL_LOWER_BOUND; - cap.srgb.range_size = tnode->srgb.range_size - ? tnode->srgb.range_size - : SRGB_DFTL_RANGE_SIZE; - cap.algo[0] = SR_ALGORITHM_SPF; - cap.algo[1] = SR_ALGORITHM_UNSET; + cap->srgb.lower_bound = tnode->srgb.lower_bound + ? tnode->srgb.lower_bound + : SRGB_DFTL_LOWER_BOUND; + cap->srgb.range_size = tnode->srgb.range_size + ? tnode->srgb.range_size + : SRGB_DFTL_RANGE_SIZE; + cap->algo[0] = SR_ALGORITHM_SPF; + cap->algo[1] = SR_ALGORITHM_UNSET; } - isis_tlvs_set_router_capability(lsp->tlvs, &cap); } static void lsp_add_mt_router_info(struct isis_lsp *lsp, diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c index 85ddfea5b5..6eb180b501 100644 --- a/tests/isisd/test_isis_spf.c +++ b/tests/isisd/test_isis_spf.c @@ -49,12 +49,13 @@ static void test_run_spf(struct vty *vty, const struct isis_topology *topology, /* Run SPF. */ spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD; spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree, - spf_type, F_SPFTREE_NO_ADJACENCIES); + spf_type, F_SPFTREE_NO_ADJACENCIES, + SR_ALGORITHM_SPF); isis_run_spf(spftree); /* Print the SPT and the corresponding routing table. */ isis_print_spftree(vty, spftree); - isis_print_routes(vty, spftree, false, false); + isis_print_routes(vty, spftree, NULL, false, false); /* Cleanup SPF tree. */ isis_spftree_del(spftree); @@ -71,8 +72,9 @@ static void test_run_lfa(struct vty *vty, const struct isis_topology *topology, /* 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); + spftree_self = + isis_spftree_new(area, lspdb, root->sysid, level, tree, + SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF); isis_run_spf(spftree_self); /* Run forward SPF on all adjacent routers. */ @@ -84,9 +86,9 @@ static void test_run_lfa(struct vty *vty, const struct isis_topology *topology, /* 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); + isis_print_routes(vty, spftree_self, NULL, false, false); vty_out(vty, "Backup:\n"); - isis_print_routes(vty, spftree_self, false, true); + isis_print_routes(vty, spftree_self, NULL, false, true); /* Cleanup everything. */ isis_spftree_del(spftree_self); @@ -107,8 +109,9 @@ static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology, /* 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); + spftree_self = + isis_spftree_new(area, lspdb, root->sysid, level, tree, + SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF); isis_run_spf(spftree_self); /* Run reverse SPF in the root node. */ @@ -162,9 +165,9 @@ static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology, /* 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); + isis_print_routes(vty, spftree_self, NULL, false, false); vty_out(vty, "Backup:\n"); - isis_print_routes(vty, spftree_self, false, true); + isis_print_routes(vty, spftree_self, NULL, false, true); /* Cleanup everything. */ isis_spftree_del(spftree_self); @@ -187,8 +190,9 @@ static void test_run_ti_lfa(struct vty *vty, /* 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); + spftree_self = + isis_spftree_new(area, lspdb, root->sysid, level, tree, + SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF); isis_run_spf(spftree_self); /* Run reverse SPF in the root node. */ @@ -224,7 +228,7 @@ static void test_run_ti_lfa(struct vty *vty, * Print the post-convergence SPT and the corresponding routing table. */ isis_print_spftree(vty, spftree_pc); - isis_print_routes(vty, spftree_self, false, true); + isis_print_routes(vty, spftree_self, NULL, false, true); /* Cleanup everything. */ isis_spftree_del(spftree_self); diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c index 8f54856186..4ad41fca42 100644 --- a/tests/lib/cxxcompat.c +++ b/tests/lib/cxxcompat.c @@ -25,7 +25,6 @@ #include "lib/frr_pthread.h" #include "lib/frratomic.h" #include "lib/frrstr.h" -#include "lib/getopt.h" #include "lib/graph.h" #include "lib/hash.h" #include "lib/hook.h" diff --git a/tests/lib/subdir.am b/tests/lib/subdir.am index 1bc092a49e..e950d0120d 100644 --- a/tests/lib/subdir.am +++ b/tests/lib/subdir.am @@ -15,7 +15,7 @@ tests_lib_test_frrscript_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_frrscript_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_frrscript_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_frrscript_SOURCES = tests/lib/test_frrscript.c -EXTRA_DIST += tests/lib/test_frrscript.py +EXTRA_DIST += tests/lib/test_frrscript.py tests/lib/script1.lua ############################################################################## diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h index 91528139b5..80c4005437 100644 --- a/tests/lib/test_typelist.h +++ b/tests/lib/test_typelist.h @@ -171,6 +171,11 @@ static void concat(test_, TYPE)(void) ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); +#if !IS_ATOMIC(REALTYPE) + assert(!list_member(&head, &itm[0])); + assert(!list_member(&head, &itm[1])); +#endif + #if IS_SORTED(REALTYPE) prng = prng_new(0); k = 0; diff --git a/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 b/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 index 6d870f355f..9ce2f2e825 100644 --- a/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 +++ b/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 @@ -1,9 +1,9 @@ log file ospf6d.log ! -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor +!debug ospf6 lsa unknown +!debug ospf6 zebra +!debug ospf6 interface +!debug ospf6 neighbor ! interface r1-eth4 ! 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 f7c3a4c19d..92bb99c8f2 100644 --- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -288,6 +288,17 @@ def test_converge_protocols(): thisDir = os.path.dirname(os.path.realpath(__file__)) + # We need loopback to have a link local so it always is the + # "selected" router for fe80::/64 when we static compare below. + print("Adding link-local to loopback for stable results") + cmd = ( + "mac=`cat /sys/class/net/lo/address`; echo lo: $mac;" + " [ -z \"$mac\" ] && continue; IFS=':'; set $mac; unset IFS;" + " ip address add dev lo scope link" + " fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64" + ) + net["r1"].cmd_raises(cmd) + print("\n\n** Waiting for protocols convergence") print("******************************************\n") diff --git a/tests/topotests/bfd_topo3/r5/bfdd.conf b/tests/topotests/bfd_topo3/r5/bfdd.conf index 6d4483acc4..ec62d8d275 100644 --- a/tests/topotests/bfd_topo3/r5/bfdd.conf +++ b/tests/topotests/bfd_topo3/r5/bfdd.conf @@ -1,6 +1,6 @@ -debug bfd network -debug bfd peer -debug bfd zebra +!debug bfd network +!debug bfd peer +!debug bfd zebra ! bfd profile slow-tx diff --git a/tests/topotests/bfd_topo3/r6/bfdd.conf b/tests/topotests/bfd_topo3/r6/bfdd.conf index 6d4483acc4..ec62d8d275 100644 --- a/tests/topotests/bfd_topo3/r6/bfdd.conf +++ b/tests/topotests/bfd_topo3/r6/bfdd.conf @@ -1,6 +1,6 @@ -debug bfd network -debug bfd peer -debug bfd zebra +!debug bfd network +!debug bfd peer +!debug bfd zebra ! bfd profile slow-tx diff --git a/tests/topotests/bgp_accept_own/ce1/bgpd.conf b/tests/topotests/bgp_accept_own/ce1/bgpd.conf index fa53a42919..44f95b9bb3 100644 --- a/tests/topotests/bgp_accept_own/ce1/bgpd.conf +++ b/tests/topotests/bgp_accept_own/ce1/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp updates +!debug bgp updates ! router bgp 65010 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_accept_own/ce2/bgpd.conf b/tests/topotests/bgp_accept_own/ce2/bgpd.conf index cdf8898c90..d60fdcf7cb 100644 --- a/tests/topotests/bgp_accept_own/ce2/bgpd.conf +++ b/tests/topotests/bgp_accept_own/ce2/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp updates +!debug bgp updates ! router bgp 65020 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_accept_own/pe1/bgpd.conf b/tests/topotests/bgp_accept_own/pe1/bgpd.conf index 109e0eadbb..15466b4259 100644 --- a/tests/topotests/bgp_accept_own/pe1/bgpd.conf +++ b/tests/topotests/bgp_accept_own/pe1/bgpd.conf @@ -1,8 +1,8 @@ ! -debug bgp updates -debug bgp vpn leak-from-vrf -debug bgp vpn leak-to-vrf -debug bgp nht +!debug bgp updates +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp nht ! router bgp 65001 bgp router-id 10.10.10.10 diff --git a/tests/topotests/bgp_accept_own/rr1/bgpd.conf b/tests/topotests/bgp_accept_own/rr1/bgpd.conf index 4f0a6ab0f1..ad0ee3e471 100644 --- a/tests/topotests/bgp_accept_own/rr1/bgpd.conf +++ b/tests/topotests/bgp_accept_own/rr1/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp updates +!debug bgp updates ! router bgp 65001 bgp router-id 10.10.10.101 diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf index 2071c256da..e855f75c20 100644 --- a/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf +++ b/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf @@ -2,7 +2,9 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as external neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 timers connect 1 neighbor 192.168.255.2 bfd + neighbor 192.168.255.2 passive address-family ipv4 redistribute connected exit-address-family diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf index 3279804e6e..faf2c6b39b 100644 --- a/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf +++ b/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65002 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as external neighbor 192.168.255.1 timers 3 10 + neighbor 192.168.255.1 timers connect 1 neighbor 192.168.255.1 bfd address-family ipv4 redistribute connected diff --git a/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py b/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py index 0bc0306d7d..00142981c5 100644 --- a/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py +++ b/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py @@ -88,13 +88,14 @@ def test_bgp_bfd_down_notification(): expected = { "192.168.255.1": { "lastNotificationReason": "Cease/BFD Down", + "lastNotificationHardReset": True, } } return topotest.json_cmp(output, expected) step("Initial BGP converge") test_func = functools.partial(_bgp_converge) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "Failed to see BGP convergence on R2" step("Kill bfdd on R2") @@ -102,7 +103,7 @@ def test_bgp_bfd_down_notification(): step("Check if we received Cease/BFD Down notification message") test_func = functools.partial(_bgp_bfd_down_notification) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "Failed to see BGP Cease/BFD Down notification message on R2" diff --git a/tests/topotests/bgp_comm_list_match/r2/bgpd.conf b/tests/topotests/bgp_comm_list_match/r2/bgpd.conf index 35ad2d32e6..98a9780688 100644 --- a/tests/topotests/bgp_comm_list_match/r2/bgpd.conf +++ b/tests/topotests/bgp_comm_list_match/r2/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp updates +!debug bgp updates ! router bgp 65002 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_confed1/r1/bgpd.conf b/tests/topotests/bgp_confed1/r1/bgpd.conf index 8413ef7fc3..107d2ad8d2 100644 --- a/tests/topotests/bgp_confed1/r1/bgpd.conf +++ b/tests/topotests/bgp_confed1/r1/bgpd.conf @@ -1,7 +1,7 @@ -debug bgp neighbor-events -debug bgp nht -debug bgp updates in -debug bgp updates out +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out ! router bgp 100 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_confed1/r2/bgpd.conf b/tests/topotests/bgp_confed1/r2/bgpd.conf index 9f6a9852de..fe13dfe729 100644 --- a/tests/topotests/bgp_confed1/r2/bgpd.conf +++ b/tests/topotests/bgp_confed1/r2/bgpd.conf @@ -1,7 +1,7 @@ -debug bgp neighbor-events -debug bgp nht -debug bgp updates in -debug bgp updates out +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out ! router bgp 200 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_confed1/r3/bgpd.conf b/tests/topotests/bgp_confed1/r3/bgpd.conf index 3a018a42b3..74d5fd6e29 100644 --- a/tests/topotests/bgp_confed1/r3/bgpd.conf +++ b/tests/topotests/bgp_confed1/r3/bgpd.conf @@ -1,7 +1,7 @@ -debug bgp neighbor-events -debug bgp nht -debug bgp updates in -debug bgp updates out +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out ! router bgp 300 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_confed1/r4/bgpd.conf b/tests/topotests/bgp_confed1/r4/bgpd.conf index 134f221543..89a85e5a34 100644 --- a/tests/topotests/bgp_confed1/r4/bgpd.conf +++ b/tests/topotests/bgp_confed1/r4/bgpd.conf @@ -1,7 +1,7 @@ -debug bgp neighbor-events -debug bgp nht -debug bgp updates in -debug bgp updates out +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out ! router bgp 400 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf b/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf index e2ff1df965..2f76d59d4a 100644 --- a/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf +++ b/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp neighbor +!debug bgp neighbor ! router bgp 65001 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py index f8af210ed7..65c0c3532a 100755 --- a/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py @@ -83,6 +83,7 @@ def build_topo(tgen): switch.add_link(tgen.gears["PE2"]) switch.add_link(tgen.gears["host2"]) + def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe = tgen.gears[pe_name] @@ -100,7 +101,9 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): # setup single vxlan device pe.run( - "ip link add dev vxlan0 type vxlan dstport 4789 local {0} nolearning external".format(tunnel_local_ip) + "ip link add dev vxlan0 type vxlan dstport 4789 local {0} nolearning external".format( + tunnel_local_ip + ) ) pe.run("ip link set dev vxlan0 master bridge") pe.run("bridge link set dev vxlan0 vlan_tunnel on") @@ -136,10 +139,12 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe.run("bridge vlan add dev vxlan0 vid 300") pe.run("bridge vlan add dev vxlan0 vid 300 tunnel_info id 300") + def setup_p_router(tgen, p_name): p1 = tgen.gears[p_name] p1.run("sysctl -w net.ipv4.ip_forward=1") + def setup_module(mod): "Sets up the pytest environment" @@ -180,7 +185,7 @@ def teardown_module(mod): "Teardown the pytest environment" tgen = get_topogen() - #tgen.mininet_cli() + # tgen.mininet_cli() # This function tears down the whole topology. tgen.stop_topology() @@ -204,17 +209,21 @@ def check_vni_macs_present(tgen, router, vni, maclist): ) return None + def check_flood_entry_present(pe, vni, vtep): if not topotest.iproute2_is_fdb_get_capable(): return None - output = pe.run("bridge fdb get 00:00:00:00:00:00 dev vxlan0 vni {} self".format(vni)) + output = pe.run( + "bridge fdb get 00:00:00:00:00:00 dev vxlan0 vni {} self".format(vni) + ) if str(vtep) not in output: return output return None + def test_pe1_converge_evpn(): "Wait for protocol convergence" @@ -231,6 +240,15 @@ def test_pe1_converge_evpn(): _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + # Let's ensure that the hosts have actually tried talking to + # each other. Otherwise under certain startup conditions + # they may not actually do any l2 arp'ing and as such + # the bridges won't know about the hosts on their networks + host1 = tgen.gears["host1"] + host1.run("ping -c 1 10.10.1.56") + host2 = tgen.gears["host2"] + host2.run("ping -c 1 10.10.1.55") + test_func = partial( check_vni_macs_present, tgen, @@ -249,11 +267,12 @@ def test_pe1_converge_evpn(): assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe1.name, vtep) assert result is None, assertmsg + def test_pe2_converge_evpn(): "Wait for protocol convergence" tgen = get_topogen() -#Don't run this test if we have any failure. + # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) @@ -284,6 +303,7 @@ def test_pe2_converge_evpn(): assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe2.name, vtep) assert result is None, assertmsg + def mac_learn_test(host, local): "check the host MAC gets learned by the VNI" @@ -389,11 +409,11 @@ def ip_learn_test(tgen, host, local, remote, ip_addr): if "HWaddr" in line_items[0]: mac = line_items[1] break - #print(host_output) + # print(host_output) # check we have a local association between the MAC and IP local_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) - #print(local_output) + # print(local_output) local_output_json = json.loads(local_output) mac_type = local_output_json[mac]["type"] assertmsg = "Failed to learn local IP address on host {}".format(host.name) @@ -417,7 +437,7 @@ def ip_learn_test(tgen, host, local, remote, ip_addr): remote_output = remote.vtysh_cmd( "show evpn mac vni 101 mac {} json".format(mac) ) - #print(remote_output) + # print(remote_output) remote_output_json = json.loads(remote_output) type = remote_output_json[mac]["type"] if not remote_output_json[mac]["neighbors"] == "none": @@ -431,12 +451,12 @@ def ip_learn_test(tgen, host, local, remote, ip_addr): count += 1 sleep(1) - #print("tries: {}".format(count)) + # print("tries: {}".format(count)) assertmsg = "{} remote learned mac no address: {} ".format(host.name, mac) # some debug for this failure if not converged == True: log_output = remote.run("cat zebra.log") - #print(log_output) + # print(log_output) assert converged == True, assertmsg if remote_output_json[mac]["neighbors"]["active"]: @@ -463,8 +483,8 @@ def test_ip_pe1_learn(): host1 = tgen.gears["host1"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - pe2.vtysh_cmd("debug zebra vxlan") - pe2.vtysh_cmd("debug zebra kernel") + # pe2.vtysh_cmd("debug zebra vxlan") + # pe2.vtysh_cmd("debug zebra kernel") # lets populate that arp cache host1.run("ping -c1 10.10.1.1") ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55") @@ -482,13 +502,14 @@ def test_ip_pe2_learn(): host2 = tgen.gears["host2"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - pe1.vtysh_cmd("debug zebra vxlan") - pe1.vtysh_cmd("debug zebra kernel") + # pe1.vtysh_cmd("debug zebra vxlan") + # pe1.vtysh_cmd("debug zebra kernel") # lets populate that arp cache host2.run("ping -c1 10.10.1.3") ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56") # tgen.mininet_cli() + def show_dvni_route(pe, vni, prefix, vrf): output = pe.vtysh_cmd("show ip route vrf {} {}".format(vrf, prefix)) @@ -502,6 +523,7 @@ def show_dvni_route(pe, vni, prefix, vrf): return None + def test_dvni(): "test Downstream VNI works as expected importing into PE1" @@ -517,7 +539,7 @@ def test_dvni(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assertmsg = '"{}" DVNI route {} not found'.format(pe1.name, prefix) assert result is None, assertmsg - #tgen.mininet_cli() + # tgen.mininet_cli() def test_memory_leak(): 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 48b79ab5ee..2884043012 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 @@ -164,6 +164,15 @@ def test_pe1_converge_evpn(): _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + # Let's ensure that the hosts have actually tried talking to + # each other. Otherwise under certain startup conditions + # they may not actually do any l2 arp'ing and as such + # the bridges won't know about the hosts on their networks + host1 = tgen.gears["host1"] + host1.run("ping -c 1 10.10.1.56") + host2 = tgen.gears["host2"] + host2.run("ping -c 1 10.10.1.55") + test_func = partial( check_vni_macs_present, tgen, @@ -171,6 +180,7 @@ def test_pe1_converge_evpn(): 101, (("host1", "host1-eth0"), ("host2", "host2-eth0")), ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) if result: logger.warning("%s", result) @@ -385,8 +395,8 @@ def test_ip_pe1_learn(): host1 = tgen.gears["host1"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - pe2.vtysh_cmd("debug zebra vxlan") - pe2.vtysh_cmd("debug zebra kernel") + # pe2.vtysh_cmd("debug zebra vxlan") + # pe2.vtysh_cmd("debug zebra kernel") # lets populate that arp cache host1.run("ping -c1 10.10.1.1") ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55") @@ -404,8 +414,8 @@ def test_ip_pe2_learn(): host2 = tgen.gears["host2"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - pe1.vtysh_cmd("debug zebra vxlan") - pe1.vtysh_cmd("debug zebra kernel") + # pe1.vtysh_cmd("debug zebra vxlan") + # pe1.vtysh_cmd("debug zebra kernel") # lets populate that arp cache host2.run("ping -c1 10.10.1.3") ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56") diff --git a/tests/topotests/bgp_features/r1/ospf_neighbor.json b/tests/topotests/bgp_features/r1/ospf_neighbor.json index 3b5f46d934..caf700d82a 100644 --- a/tests/topotests/bgp_features/r1/ospf_neighbor.json +++ b/tests/topotests/bgp_features/r1/ospf_neighbor.json @@ -2,13 +2,13 @@ "neighbors":{ "192.168.0.2":[ { - "priority":5, + "nbrPriority":5, "converged":"Full" } ], "192.168.0.3":[ { - "priority":5, + "nbrPriority":5, "converged":"Full" } ] diff --git a/tests/topotests/bgp_features/r2/ospf_neighbor.json b/tests/topotests/bgp_features/r2/ospf_neighbor.json index 47bb57cd00..3a168ba335 100644 --- a/tests/topotests/bgp_features/r2/ospf_neighbor.json +++ b/tests/topotests/bgp_features/r2/ospf_neighbor.json @@ -2,13 +2,13 @@ "neighbors":{ "192.168.0.1":[ { - "priority":10, + "nbrPriority":10, "converged":"Full" } ], "192.168.0.3":[ { - "priority":5, + "nbrPriority":5, "converged":"Full" } ] diff --git a/tests/topotests/bgp_features/r3/ospf_neighbor.json b/tests/topotests/bgp_features/r3/ospf_neighbor.json index b84974ccca..9f8c05949f 100644 --- a/tests/topotests/bgp_features/r3/ospf_neighbor.json +++ b/tests/topotests/bgp_features/r3/ospf_neighbor.json @@ -2,13 +2,13 @@ "neighbors":{ "192.168.0.1":[ { - "priority":10, + "nbrPriority":10, "converged":"Full" } ], "192.168.0.2":[ { - "priority":10, + "nbrPriority":10, "converged":"Full" } ] diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py index 6388295c95..1a8f8302ff 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py @@ -1159,7 +1159,7 @@ def test_BGP_GR_TC_31_2_p1(request): reset_config_on_routers(tgen) logger.info( - "[Phase 1] : Test Setup " "[Disable Mode]R1-----R2[Restart Mode] initialized " + "[Phase 1] : Test Setup " "[Disable Mode]R1-----R2[Helper Mode] initialized " ) # Configure graceful-restart @@ -1251,7 +1251,7 @@ def test_BGP_GR_TC_31_2_p1(request): tc_name, result ) - logger.info("[Phase 2] : R2 Goes from Disable to Restart Mode ") + logger.info("[Phase 2] : R1 Goes from Disable to Restart Mode ") # Configure graceful-restart input_dict = { @@ -1356,31 +1356,7 @@ def test_BGP_GR_TC_31_2_p1(request): }, } - # here the verify_graceful_restart fro the neighbor would be - # "NotReceived" as the latest GR config is not yet applied. - for addr_type in ADDR_TYPES: - result = verify_graceful_restart( - tgen, topo, addr_type, input_dict, dut="r1", peer="r2" - ) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for addr_type in ADDR_TYPES: - # Verifying RIB routes - next_hop = next_hop_per_address_family( - tgen, dut, peer, addr_type, NEXT_HOP_IP_2 - ) - input_topo = {key: topo["routers"][key] for key in ["r2"]} - result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - logger.info("[Phase 6] : R1 is about to come up now ") - start_router_daemons(tgen, "r1", ["bgpd"]) - - logger.info("[Phase 4] : R1 is UP now, so time to collect GR stats ") + logger.info("[Phase 4] : R1 is UP and GR state is correct ") for addr_type in ADDR_TYPES: result = verify_graceful_restart( @@ -2142,6 +2118,9 @@ def test_BGP_GR_TC_43_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) @@ -2432,6 +2411,9 @@ def test_BGP_GR_TC_44_p1(request): result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r2", ["bgpd"]) + write_test_footer(tc_name) @@ -2727,6 +2709,9 @@ def test_BGP_GR_TC_45_p1(request): result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py index 1166cdc0ef..31aaa0b8a6 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py @@ -763,6 +763,9 @@ def test_BGP_GR_TC_46_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) @@ -1023,6 +1026,9 @@ def test_BGP_GR_TC_47_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) @@ -1300,6 +1306,9 @@ def test_BGP_GR_TC_48_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py index eaa6aa4c30..46993c7d9a 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py @@ -62,6 +62,25 @@ else: "pass", "Adding {} routes".format(num), ) + luCommand( + "ce1", + 'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33', + str(num), + "wait", + "See all sharp routes in rib on ce1", + wait, + wait_time=10, + ) + luCommand( + "ce2", + 'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33', + str(num), + "wait", + "See all sharp routes in rib on ce2", + wait, + wait_time=10, + ) + rtrs = ["ce1", "ce2", "ce3"] for rtr in rtrs: luCommand( diff --git a/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py b/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py index 3af779c427..f4bb487e40 100644 --- a/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py +++ b/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py @@ -82,53 +82,23 @@ def test_bgp_addpath_labeled_unicast(): r3 = tgen.gears["r3"] r4 = tgen.gears["r4"] - def _bgp_check_advertised_routes(prefix_num): - output = json.loads( - r3.vtysh_cmd( - "show bgp ipv4 labeled-unicast neighbors 192.168.34.4 advertised-routes json" - ) - ) + def _bgp_check_received_routes(pfxcount): + output = json.loads(r4.vtysh_cmd("show bgp ipv4 labeled-unicast summary json")) expected = { - "advertisedRoutes": { - "10.0.0.1/32": { - "appliedStatusSymbols": { - "*": True, - ">": True, - "=": True, - } + "peers": { + "192.168.34.3": { + "pfxRcd": pfxcount, + "state": "Established", } - }, - "totalPrefixCounter": prefix_num, + } } return topotest.json_cmp(output, expected) - test_func = functools.partial(_bgp_check_advertised_routes, 2) + test_func = functools.partial(_bgp_check_received_routes, 2) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert ( result is None - ), "Failed to advertise labeled-unicast with addpath (multipath)" - - def _bgp_check_received_routes(): - output = json.loads(r4.vtysh_cmd("show bgp ipv4 labeled-unicast json")) - expected = { - "routes": { - "10.0.0.1/32": [ - { - "valid": True, - "path": "65003 65001", - }, - { - "valid": True, - "path": "65003 65002", - }, - ] - } - } - return topotest.json_cmp(output, expected) - - test_func = functools.partial(_bgp_check_received_routes) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) - assert result is None, "Failed to receive labeled-unicast with addpath (multipath)" + ), "Failed to receive labeled-unicast with addpath (multipath=2)" step("Enable BGP session for R5") r3.vtysh_cmd( @@ -139,11 +109,11 @@ def test_bgp_addpath_labeled_unicast(): """ ) - test_func = functools.partial(_bgp_check_advertised_routes, 3) + test_func = functools.partial(_bgp_check_received_routes, 3) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert ( result is None - ), "Failed to advertise labeled-unicast with addpath (multipath)" + ), "Failed to receive labeled-unicast with addpath (multipath=3)" step("Disable BGP session for R5") r3.vtysh_cmd( @@ -154,11 +124,11 @@ def test_bgp_addpath_labeled_unicast(): """ ) - test_func = functools.partial(_bgp_check_advertised_routes, 2) + test_func = functools.partial(_bgp_check_received_routes, 2) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert ( result is None - ), "Failed to advertise labeled-unicast with addpath (multipath)" + ), "Failed to receive labeled-unicast with addpath (multipath=2)" if __name__ == "__main__": diff --git a/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf new file mode 100644 index 0000000000..a31439c984 --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + bgp labeled-unicast explicit-null + neighbor 192.0.2.2 remote-as 65501 +! + address-family ipv4 unicast + no neighbor 192.0.2.2 activate + network 192.168.2.1/32 + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.0.2.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf new file mode 100644 index 0000000000..b84574891e --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf @@ -0,0 +1,6 @@ +interface r1-eth0 + ip address 192.0.2.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/32 +!
\ No newline at end of file diff --git a/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf new file mode 100644 index 0000000000..41c2b9b6fa --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65501 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + bgp labeled-unicast explicit-null + neighbor 192.0.2.1 remote-as 65500 +! + address-family ipv4 unicast + no neighbor 192.0.2.1 activate + network 192.168.2.2/32 + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.0.2.1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf new file mode 100644 index 0000000000..9a639610c1 --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf @@ -0,0 +1,6 @@ +interface r2-eth0 + ip address 192.0.2.2/24 +! +interface r2-eth1 + ip address 192.168.2.2/32 +! diff --git a/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py b/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py new file mode 100644 index 0000000000..0656e1ed41 --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_bgp_explicitnull.py +# +# Part of NetDEF Topology Tests +# +# Copyright 2023 by 6WIND S.A. +# + +""" +test_bgp_lu_explicitnull.py: Test BGP LU label allocation +""" + +import os +import sys +import json +import functools +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 + + +pytestmark = [pytest.mark.bgpd] + + +# Basic scenario for BGP-LU. Nodes are directly connected. +# The 192.168.2.2/32 prefix is advertised from r2 to r1 +# The explicit-null label should be used +# The 192.168.2.1/32 prefix is advertised from r1 to r2 +# The explicit-null label should be used +# Traffic from 192.168.2.1 to 192.168.2.2 should use explicit-null label +# +# AS65500 BGP-LU AS65501 +# +-----+ +-----+ +# | |.1 .2| | +# | 1 +----------------+ 2 + 192.168.0.2/32 +# | | 192.0.2.0/24 | | +# +-----+ +-----+ + + +def build_topo(tgen): + "Build function" + + # Create routers + tgen.add_router("r1") + tgen.add_router("r2") + + # r1-r2 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # r1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + # r2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + + # Skip if no mpls support + if not tgen.hasmpls: + logger.info("MPLS is not available, skipping test") + pytest.skip("MPLS is not available, skipping") + return + + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # Enable mpls input for routers, so we can ping + sval = "net.mpls.conf.{}.input" + topotest.sysctl_assure(router_list["r2"], sval.format("r2-eth0"), 1) + topotest.sysctl_assure(router_list["r1"], sval.format("r1-eth0"), 1) + + # 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_show_ip_label_prefix_found(router, ipversion, prefix, label): + output = json.loads( + router.vtysh_cmd("show {} route {} json".format(ipversion, prefix)) + ) + expected = { + prefix: [ + {"prefix": prefix, "nexthops": [{"fib": True, "labels": [int(label)]}]} + ] + } + return topotest.json_cmp(output, expected) + + +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 r1 gets prefix 192.168.2.2/32 + test_func = functools.partial( + check_show_ip_label_prefix_found, + tgen.gears["r1"], + "ip", + "192.168.2.2/32", + "0", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, prefix 192.168.2.2/32 from r2 not present" + + # Check r2 gets prefix 192.168.2.1/32 + test_func = functools.partial( + check_show_ip_label_prefix_found, + tgen.gears["r2"], + "ip", + "192.168.2.1/32", + "0", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, prefix 192.168.2.1/32 from r1 not present" + + +def test_traffic_connectivity(): + "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) + + def _check_ping(name, dest_addr, src_addr): + tgen = get_topogen() + output = tgen.gears[name].run( + "ping {} -c 1 -w 1 -I {}".format(dest_addr, src_addr) + ) + logger.info(output) + if " 0% packet loss" not in output: + return True + + logger.info("r1, check ping 192.168.2.2 from 192.168.2.1 is OK") + tgen = get_topogen() + func = functools.partial(_check_ping, "r1", "192.168.2.2", "192.168.2.1") + # tgen.mininet_cli() + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "r1, ping to 192.168.2.2 from 192.168.2.1 fails" + + +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_node_target_extcommunities/__init__.py b/tests/topotests/bgp_node_target_extcommunities/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/__init__.py diff --git a/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf new file mode 100644 index 0000000000..86983387d5 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf @@ -0,0 +1,21 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router bgp 65001 + bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.3 remote-as external + neighbor 192.168.1.4 remote-as external + address-family ipv4 unicast + network 10.10.10.10/32 + neighbor 192.168.1.2 route-map rmap out + neighbor 192.168.1.3 route-map rmap out + neighbor 192.168.1.4 route-map rmap out + exit-address-family +! +route-map rmap permit 10 + set extcommunity nt 192.168.1.3:0 192.168.1.4:0 +exit diff --git a/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf new file mode 100644 index 0000000000..09fda78a3d --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf @@ -0,0 +1,8 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! +router bgp 65002 + bgp router-id 192.168.1.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf new file mode 100644 index 0000000000..4883f1f5c2 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf @@ -0,0 +1,8 @@ +! +int r3-eth0 + ip address 192.168.1.3/24 +! +router bgp 65003 + bgp router-id 192.168.1.3 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf new file mode 100644 index 0000000000..f518bd1fa0 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf @@ -0,0 +1,8 @@ +! +int r4-eth0 + ip address 192.168.1.4/24 +! +router bgp 65004 + bgp router-id 192.168.1.4 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py b/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py new file mode 100644 index 0000000000..23e820b4fc --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if Node Target Extended Communities works. + +At r1 we set NT to 192.168.1.3 and 192.168.1.4 (this is the R3/R4 router-id), +and that means 10.10.10.10/32 MUST be installed on R3 and R4, but not on R2, +because this route does not have NT:192.168.1.2. +""" + +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 + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + 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.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_node_target_extended_communities(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": { + "192.168.1.2": { + "pfxSnt": 1, + "state": "Established", + }, + "192.168.1.3": { + "pfxSnt": 1, + "state": "Established", + }, + "192.168.1.4": { + "pfxSnt": 1, + "state": "Established", + }, + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed announcing 10.10.10.10/32 to r2, r3, and r4" + + def _bgp_check_route(router, exists): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + if exists: + expected = { + "routes": { + "10.10.10.10/32": [ + { + "valid": True, + } + ] + } + } + else: + expected = { + "routes": { + "10.10.10.10/32": None, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_route, r3, True) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.10/32 is not installed, but SHOULD be" + + test_func = functools.partial(_bgp_check_route, r4, True) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.10/32 is not installed, but SHOULD be" + + test_func = functools.partial(_bgp_check_route, r2, False) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.10/32 is installed, but SHOULD NOT be" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf b/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf index 77f7c5581b..733205928f 100644 --- a/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf +++ b/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp updates +!debug bgp updates ! router bgp 65002 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_route_map_delay_timer/__init__.py b/tests/topotests/bgp_route_map_delay_timer/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/__init__.py diff --git a/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf b/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf new file mode 100644 index 0000000000..e5325c91bc --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf @@ -0,0 +1,24 @@ +! +!debug bgp updates +!debug bgp neighbor +! +bgp route-map delay-timer 5 +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + address-family ipv4 unicast + network 10.10.10.1/32 + network 10.10.10.2/32 + network 10.10.10.3/32 + aggregate-address 10.10.10.0/24 summary-only + neighbor 192.168.1.2 unsuppress-map r2 + exit-address-family +! +ip prefix-list r1 seq 5 permit 10.10.10.1/32 +ip prefix-list r1 seq 10 permit 10.10.10.2/32 +! +route-map r2 permit 10 + match ip address prefix-list r1 +exit diff --git a/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf b/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf new file mode 100644 index 0000000000..b29940f46a --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf b/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf new file mode 100644 index 0000000000..36653e6b1c --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external +! diff --git a/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf b/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf new file mode 100644 index 0000000000..cffe827363 --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py b/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py new file mode 100644 index 0000000000..15a077da2e --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" + +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +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 + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, 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_route_map_delay_timer(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge_1(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.0/24": {}, + "10.10.10.1/32": {}, + "10.10.10.2/32": {}, + "10.10.10.3/32": None, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge_1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "10.10.10.3/32 should not be advertised to r2" + + # Set route-map delay-timer to max value and remove 10.10.10.2/32. + # After this, r1 MUST do not announce updates immediately, and wait + # 600 seconds before withdrawing 10.10.10.2/32. + r2.vtysh_cmd( + """ + configure terminal + bgp route-map delay-timer 600 + no ip prefix-list r1 seq 10 permit 10.10.10.2/32 + """ + ) + + def _bgp_converge_2(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.0/24": {}, + "10.10.10.1/32": {}, + "10.10.10.2/32": None, + "10.10.10.3/32": None, + } + } + return topotest.json_cmp(output, expected) + + # We are checking `not None` here to wait count*wait time and if we have different + # results than expected, it means good - 10.10.10.2/32 wasn't withdrawn immediately. + test_func = functools.partial(_bgp_converge_2) + _, result = topotest.run_and_expect(test_func, not None, count=60, wait=0.5) + assert ( + result is not None + ), "10.10.10.2/32 advertised, but should not be advertised to r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf b/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf index c9ad0b1a5b..4aa11ec9d0 100644 --- a/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf +++ b/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf @@ -1,9 +1,9 @@ ! -debug bgp updates -debug bgp vpn leak-from-vrf -debug bgp vpn leak-to-vrf -debug bgp nht -debug route-map +!debug bgp updates +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp nht +!debug route-map ! router bgp 65001 bgp router-id 10.10.10.10 diff --git a/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf b/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf new file mode 100644 index 0000000000..1929dfa69b --- /dev/null +++ b/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf @@ -0,0 +1,2 @@ +router bgp 65001 + neighbor 192.168.2.1 remote-as external diff --git a/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py b/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py new file mode 100644 index 0000000000..673efc2c73 --- /dev/null +++ b/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 03 2023, Trey Aspelund <taspelund@nvidia.com> +# +# Copyright (C) 2023 NVIDIA Corporation +# +# Test if the CLI parser for RT/SoO ecoms correctly +# constrain user input to valid 4-byte ASN values. +# + +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.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("pe1") + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + pe1 = tgen.gears["pe1"] + pe1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "pe1/bgpd.conf")) + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_origin_parser(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["pe1"] + + def _invalid_soo_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + neighbor 192.168.2.1 soo 4294967296:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "soo" in run_cfg + + def _max_soo_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + neighbor 192.168.2.1 soo 4294967295:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "soo 4294967295:65" in run_cfg + + def _invalid_rt_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + rt vpn both 4294967296:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "rt vpn" in run_cfg + + def _max_rt_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + rt vpn both 4294967295:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "rt vpn both 4294967295:65" in run_cfg + + step( + "Configure invalid 4-byte value SoO (4294967296:65), this should not be accepted" + ) + test_func = functools.partial(_invalid_soo_accepted) + _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5) + assert result is False, "invalid 4-byte value of SoO accepted" + + step("Configure max 4-byte value SoO (4294967295:65), this should be accepted") + test_func = functools.partial(_max_soo_accepted) + _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5) + assert result is True, "max 4-byte value of SoO not accepted" + + step( + "Configure invalid 4-byte value RT (4294967296:65), this should not be accepted" + ) + test_func = functools.partial(_invalid_rt_accepted) + _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5) + assert result is False, "invalid 4-byte value of RT accepted" + + step("Configure max 4-byte value RT (4294967295:65), this should be accepted") + test_func = functools.partial(_max_rt_accepted) + _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5) + assert result is True, "max 4-byte value of RT not accepted" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf index 3512e66cec..cf0013e1b7 100644 --- a/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp updates +!debug bgp updates ! router bgp 65002 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf new file mode 100644 index 0000000000..3459796629 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce1 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json new file mode 100644 index 0000000000..d19e315772 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:1::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:1::/64": [ + { + "prefix": "2001:1::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf new file mode 100644 index 0000000000..bb5f93fe52 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce1 +! +interface eth0 + ipv6 address 2001:1::2/64 + ip address 192.168.1.2/24 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:1::1 +ip route 0.0.0.0/0 192.168.1.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf new file mode 100644 index 0000000000..8ed9978749 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce2 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json new file mode 100644 index 0000000000..35ff14efad --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:2::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:2::/64": [ + { + "prefix": "2001:2::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf new file mode 100644 index 0000000000..a52b83f2dc --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce2 +! +interface eth0 + ipv6 address 2001:2::2/64 + ip address 192.168.2.2/24 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:2::1 +ip route 0.0.0.0/0 192.168.2.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf new file mode 100644 index 0000000000..a85d9701c7 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce3 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json new file mode 100644 index 0000000000..2f2931f80f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:3::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:3::/64": [ + { + "prefix": "2001:3::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf new file mode 100644 index 0000000000..beca0b1211 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce3 +! +interface eth0 + ipv6 address 2001:3::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:3::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf new file mode 100644 index 0000000000..93fb32fd1b --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce4 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json new file mode 100644 index 0000000000..8a98768e0d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:4::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:4::/64": [ + { + "prefix": "2001:4::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf new file mode 100644 index 0000000000..7b21074df0 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce4 +! +interface eth0 + ipv6 address 2001:4::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:4::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf new file mode 100644 index 0000000000..2ab6f2d2a7 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce5 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json new file mode 100644 index 0000000000..80ff52ad6e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:5::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:5::/64": [ + { + "prefix": "2001:5::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf new file mode 100644 index 0000000000..b5ad48e709 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce5 +! +interface eth0 + ipv6 address 2001:5::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:5::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf new file mode 100644 index 0000000000..e0b6540514 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce6 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json new file mode 100644 index 0000000000..ace6136f06 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:6::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:6::/64": [ + { + "prefix": "2001:6::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf new file mode 100644 index 0000000000..7d19d9880b --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce6 +! +interface eth0 + ipv6 address 2001:6::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:6::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf new file mode 100644 index 0000000000..bfc9db960a --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf @@ -0,0 +1,79 @@ +frr defaults traditional +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 1 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001::2 remote-as 2 + neighbor 2001::2 timers 3 10 + neighbor 2001::2 timers connect 1 + neighbor 2001::2 update-source 2001::1 + neighbor 2001::2 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 2001::2 activate + exit-address-family + ! + address-family ipv6 vpn + neighbor 2001::2 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 1 vrf vrf10 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 1:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family +! + address-family ipv4 unicast + network 192.168.1.0/24 + sid vpn export auto + rd vpn export 11:10 + rt vpn both 77:77 + import vpn + export vpn + redistribute connected + exit-address-family +! +router bgp 1 vrf vrf20 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 1:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json new file mode 100644 index 0000000000..6fc43e194d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json @@ -0,0 +1,169 @@ +{ + "vrfName": "default", + "tableVersion": 2, + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json new file mode 100644 index 0000000000..9783c7e0e6 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json @@ -0,0 +1,23 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json new file mode 100644 index 0000000000..80c1acff8b --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json @@ -0,0 +1,53 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 16 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:1::" + } + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json new file mode 100644 index 0000000000..9783c7e0e6 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json @@ -0,0 +1,23 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json new file mode 100644 index 0000000000..07ca64b45b --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json @@ -0,0 +1,54 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"2001::2", + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json new file mode 100644 index 0000000000..6ac8dac25f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json @@ -0,0 +1,77 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json new file mode 100644 index 0000000000..fac3d1d5f3 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json @@ -0,0 +1,107 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:2::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json new file mode 100644 index 0000000000..69ce312c4d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json @@ -0,0 +1,77 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json new file mode 100644 index 0000000000..04e230535e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json new file mode 100644 index 0000000000..3cac156bb2 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json @@ -0,0 +1,54 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"2001::2", + "afi":"ipv6", + "labels":[ + 16 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:1::" + } + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json new file mode 100644 index 0000000000..163e9d626a --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json @@ -0,0 +1,53 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"2001::2", + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json new file mode 100644 index 0000000000..1313f20c6c --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 16 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:1::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json new file mode 100644 index 0000000000..51f249b184 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json new file mode 100644 index 0000000000..6ac8dac25f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json @@ -0,0 +1,77 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json new file mode 100644 index 0000000000..1c3dad089d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json @@ -0,0 +1,22 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json new file mode 100644 index 0000000000..9579bb15de --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json @@ -0,0 +1,112 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:2::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json new file mode 100644 index 0000000000..25f146f4b7 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json @@ -0,0 +1,106 @@ +{ + "2001:4::\/64":[ + { + "prefix":"2001:4::\/64", + "protocol":"bgp", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 48 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:3::" + } + } + ] + } + ], + "2001:5::\/64":[ + { + "prefix":"2001:5::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:6::\/64":[ + { + "prefix":"2001:6::\/64", + "protocol":"bgp", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 48 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:3::" + } + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf new file mode 100644 index 0000000000..cf31a5c11b --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf @@ -0,0 +1,43 @@ +log file zebra.log +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface eth0 + ipv6 address 2001::1/64 +! +interface eth1 vrf vrf10 + ipv6 address 2001:1::1/64 + ip address 192.168.1.1/24 +! +interface eth2 vrf vrf10 + ipv6 address 2001:3::1/64 +! +interface eth3 vrf vrf20 + ipv6 address 2001:5::1/64 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 block-len 40 node-len 24 func-bits 16 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:2:1::/64 2001::2 +ipv6 route 2001:db8:2:2::/64 2001::2 +ipv6 route 2001:db8:2:3::/64 2001::2 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf new file mode 100644 index 0000000000..892a9f73e5 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf @@ -0,0 +1,80 @@ +frr defaults traditional +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp updates +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 2 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001::1 remote-as 1 + neighbor 2001::1 update-source 2001::2 + neighbor 2001::1 timers 3 10 + neighbor 2001::1 timers connect 1 + neighbor 2001::1 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 2001::1 activate + exit-address-family + ! + address-family ipv6 vpn + neighbor 2001::1 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 2 vrf vrf10 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 2:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family +! + address-family ipv4 unicast + network 192.168.2.0/24 + sid vpn export auto + rd vpn export 22:10 + rt vpn both 77:77 + import vpn + export vpn + redistribute connected + exit-address-family +! +router bgp 2 vrf vrf20 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 2:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +!! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json new file mode 100644 index 0000000000..538e8955ef --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json @@ -0,0 +1,169 @@ +{ + "vrfName": "default", + "tableVersion": 2, + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json new file mode 100644 index 0000000000..446bb8eb3c --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:1:1:2::" + } + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:1:1:2::" + } + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json new file mode 100644 index 0000000000..8bc2fc23f1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json @@ -0,0 +1,112 @@ +{ + "2001:4::\/64":[ + { + "prefix":"2001:4::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:5::\/64":[ + { + "prefix":"2001:5::\/64", + "protocol":"bgp", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 48 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:1:1:3::" + } + } + ] + } + ], + "2001:6::\/64":[ + { + "prefix":"2001:6::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf new file mode 100644 index 0000000000..9771ee1cd7 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf @@ -0,0 +1,43 @@ +log file zebra.log +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface eth0 + ipv6 address 2001::2/64 +! +interface eth1 vrf vrf10 + ipv6 address 2001:2::1/64 + ip address 192.168.2.1/24 +! +interface eth2 vrf vrf20 + ipv6 address 2001:4::1/64 +! +interface eth3 vrf vrf20 + ipv6 address 2001:6::1/64 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:2:2::/64 block-len 40 node-len 24 func-bits 16 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:1:1::/64 2001::1 +ipv6 route 2001:db8:1:2::/64 2001::1 +ipv6 route 2001:db8:1:3::/64 2001::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py b/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py new file mode 100755 index 0000000000..cddcf6a9a1 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py @@ -0,0 +1,453 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright 2023 6WIND S.A. +# Authored by Dmytro Shytyi <dmytro.shytyi@6wind.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. +# + +import os +import re +import sys +import json +import functools +import pytest + +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.common_config import required_linux_kernel_version + + +def build_topo(tgen): + """ + CE1 CE3 CE5 + (eth0) (eth0) (eth0) + :2 :2 :2 + | | | + 192.168.1.0 | | + /24 | | + 2001: 2001: 2001: + 1::/64 3::/64 5::/64 + | | | + :1 :1 :1 + +-(eth1)--(eth2)---(eth3)-+ + | \ / | | + | (vrf10) (vrf20) | + | R1 | + +----------(eth0)---------+ + :1 + | + 2001::/64 + | + :2 + (eth0) + +----------(eth0)--------------+ + | R2 | + | (vrf10) (vrf20) | + | / / \ | + +-(eth1)-----(eth2)-----(eth3)-+ + :1 :1 :1 + | | | + +------+ +------+ +------+ + / 2001: \ / 2001: \ / 2001: \ + / 2::/64 \ 4::/64 / \ 6::/64 / + /192.168.2.0| / \ / + \ /24 / \ | | | + +------+ +------+ +------+ + | | | + :2 :2 :2 + (eth0) (eth0) (eth0) + CE2 CE4 CE6 + """ + + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("ce3") + tgen.add_router("ce4") + tgen.add_router("ce5") + tgen.add_router("ce6") + + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0") + tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce2"], tgen.gears["r2"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce3"], tgen.gears["r1"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce4"], tgen.gears["r2"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce5"], tgen.gears["r1"], "eth0", "eth3") + tgen.add_link(tgen.gears["ce6"], tgen.gears["r2"], "eth0", "eth3") + + +def setup_module(mod): + result = required_linux_kernel_version("5.11") + if result is not True: + pytest.skip("Kernel requirements are not met") + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + router_list = tgen.routers() + for i, (rname, router) in enumerate(tgen.routers().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.gears["r1"].run("modprobe vrf") + tgen.gears["r1"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r1"].run("ip link set vrf10 up") + tgen.gears["r1"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r1"].run("ip link set vrf20 up") + tgen.gears["r1"].run("ip link set eth1 master vrf10") + tgen.gears["r1"].run("ip link set eth2 master vrf10") + tgen.gears["r1"].run("ip link set eth3 master vrf20") + tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r1"].run("sysctl net.ipv4.conf.default.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.all.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.lo.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.eth0.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.eth1.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0") + + tgen.gears["r2"].run("modprobe vrf") + tgen.gears["r2"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r2"].run("ip link set vrf10 up") + tgen.gears["r2"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r2"].run("ip link set vrf20 up") + tgen.gears["r2"].run("ip link set eth1 master vrf10") + tgen.gears["r2"].run("ip link set eth2 master vrf20") + tgen.gears["r2"].run("ip link set eth3 master vrf20") + tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r2"].run("sysctl net.ipv4.conf.default.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.all.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.lo.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.eth0.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.eth1.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0") + tgen.start_router() + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + # Example: + # tgen=get_topogen() + # tgen.mininet_cli() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def check_ping(name, dest_addr, expect_connected): + def _check(name, dest_addr, match): + tgen = get_topogen() + output = tgen.gears[name].run("ping {} -c 1 -w 1".format(dest_addr)) + logger.info(output) + if match not in output: + return True + + match = ", {} packet loss".format("0%" if expect_connected else "100%") + logger.info("[+] check {} {} {}".format(name, dest_addr, match)) + tgen = get_topogen() + func = functools.partial(_check, name, dest_addr, match) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def check_rib(name, cmd, expected_file): + def _check(name, cmd, expected_file): + logger.info("polling") + tgen = get_topogen() + router = tgen.gears[name] + output = json.loads(router.vtysh_cmd(cmd)) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file)) + tgen = get_topogen() + func = functools.partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def test_rib(): + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json") + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json") + check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json") + check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json") + check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json") + check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json") + check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json") + check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json") + check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json") + check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json") + check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json") + + +def test_ping(): + check_ping("ce1", "2001:2::2", True) + check_ping("ce1", "2001:3::2", True) + check_ping("ce1", "2001:4::2", False) + check_ping("ce1", "2001:5::2", False) + check_ping("ce1", "2001:6::2", False) + check_ping("ce4", "2001:1::2", False) + check_ping("ce4", "2001:2::2", False) + check_ping("ce4", "2001:3::2", False) + check_ping("ce4", "2001:5::2", True) + check_ping("ce4", "2001:6::2", True) + + +def test_sid_per_afv6_auto(): + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json") + check_ping("ce1", "2001:2::2", True) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + no sid vpn export auto + """ + ) + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + sid vpn export auto + """ + ) + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json") + check_ping("ce1", "2001:2::2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + no sid vpn export auto + """ + ) + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + + +def test_sid_per_afv6_manual(): + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + sid vpn export 8 + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + no sid vpn export 8 + """ + ) + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + + +def test_sid_per_afv4_auto(): + check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json") + check_ping("ce1", "192.168.2.2", True) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + no sid vpn export auto + """ + ) + + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + sid vpn export auto + """ + ) + + check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json") + check_ping("ce1", "192.168.2.2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + no sid vpn export auto + """ + ) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False) + + +def test_sid_per_afv4_manual(): + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + sid vpn export 8 + """ + ) + + check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_sid_rib.json") + check_ping("ce1", "192.168.2.2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + no sid vpn export 8 + """ + ) + check_ping("ce1", "192.168.2.2", False) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json" + ) + + +def test_sid_per_vrf_auto(): + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + sid vpn per-vrf export auto + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_auto_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", True) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_auto_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + no sid vpn per-vrf export auto + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + + +def test_sid_per_vrf_manual(): + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + sid vpn per-vrf export 8 + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_manual_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", True) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_manual_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + no sid vpn per-vrf export 8 + """ + ) + + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf index a9319a6aed..cbc5ce1f09 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf @@ -7,9 +7,9 @@ log stdout notifications log monitor notifications log commands ! -debug zebra packet -debug zebra dplane -debug zebra kernel +!debug zebra packet +!debug zebra dplane +!debug zebra kernel ! interface eth0 ipv6 address 2001::1/64 diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf index 9e5fa0ac07..449ca74d5e 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf @@ -7,9 +7,9 @@ log stdout notifications log monitor notifications log commands ! -debug zebra packet -debug zebra dplane -debug zebra kernel +!debug zebra packet +!debug zebra dplane +!debug zebra kernel ! interface eth0 ipv6 address 2001::2/64 diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf index 2c560dfc06..f913b9f002 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf @@ -7,9 +7,9 @@ log stdout notifications log monitor notifications log commands ! -debug zebra packet -debug zebra dplane -debug zebra kernel +!debug zebra packet +!debug zebra dplane +!debug zebra kernel ! interface eth0 ipv6 address 2001::1/64 diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf index b9277a9a8c..201d0cce23 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf @@ -7,9 +7,9 @@ log stdout notifications log monitor notifications log commands ! -debug zebra packet -debug zebra dplane -debug zebra kernel +!debug zebra packet +!debug zebra dplane +!debug zebra kernel ! interface eth0 ipv6 address 2001::2/64 diff --git a/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf b/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf index caebb0e922..fb6980a139 100644 --- a/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf +++ b/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf @@ -2,12 +2,12 @@ access-list access seq 10 permit 192.168.1.1/32 ! ip route 192.168.1.1/32 10.0.0.10 ! -debug bgp bestpath -debug bgp nht -debug bgp updates -debug bgp update-groups -debug bgp zebra -debug zebra rib detail +!debug bgp bestpath +!debug bgp nht +!debug bgp updates +!debug bgp update-groups +!debug bgp zebra +!debug zebra rib detail ! router bgp 2 address-family ipv4 uni diff --git a/tests/topotests/bgp_suppress_fib/r2/bgpd.conf b/tests/topotests/bgp_suppress_fib/r2/bgpd.conf index 010e86aad7..129b812036 100644 --- a/tests/topotests/bgp_suppress_fib/r2/bgpd.conf +++ b/tests/topotests/bgp_suppress_fib/r2/bgpd.conf @@ -1,6 +1,6 @@ -debug bgp updates -debug bgp bestpath 40.0.0.0/8 -debug bgp zebra +!debug bgp updates +!debug bgp bestpath 40.0.0.0/8 +!debug bgp zebra ! router bgp 2 no bgp ebgp-requires-policy @@ -8,4 +8,4 @@ router bgp 2 neighbor 10.0.0.1 remote-as 1 neighbor 10.0.0.10 remote-as 3 address-family ipv4 uni - network 60.0.0.0/24
\ No newline at end of file + network 60.0.0.0/24 diff --git a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py index 47b2452b81..f89f3378fb 100644 --- a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py +++ b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py @@ -847,8 +847,6 @@ def test_bgp_unique_rid_chaos4_p2(): for intf in topo["routers"][rtr]["links"].keys(): topo1["routers"][rtr]["links"][intf].pop("ipv4") topo1["routers"][rtr]["links"][intf].pop("ipv6") - if intf is "lo": - topo1["routers"][rtr]["links"][intf].pop("ipv4") build_config_from_json(tgen, topo1, save_bkup=False) diff --git a/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf b/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf index 8d8f64158f..9f2ee19357 100644 --- a/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf +++ b/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp neighbor +!debug bgp neighbor ! router bgp 65534 vrf public bgp router-id 10.0.0.1 diff --git a/tests/topotests/config_timing/test_config_timing.py b/tests/topotests/config_timing/test_config_timing.py index 8de6f9bf70..5c1b97262c 100644 --- a/tests/topotests/config_timing/test_config_timing.py +++ b/tests/topotests/config_timing/test_config_timing.py @@ -133,7 +133,7 @@ def test_static_timing(): delta = (datetime.datetime.now() - tstamp).total_seconds() tot_delta += delta - router.logger.info( + router.logger.debug( "\nvtysh command => {}\nvtysh output <= {}\nin {}s".format( load_command, output, delta ) @@ -152,7 +152,7 @@ def test_static_timing(): # Number of static routes router = tgen.gears["r1"] - output = router.run("vtysh -h | grep address-sanitizer") + output = router.net.cmd_legacy("vtysh -h | grep address-sanitizer", warn=False) if output == "": logger.info("No Address Sanitizer, generating 10000 routes") prefix_count = 10000 diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index df5d066023..74e308dbc6 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -1,27 +1,44 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- """ Topotest conftest.py file. """ # pylint: disable=consider-using-f-string import glob +import logging import os -import pdb import re +import resource import subprocess import sys import time -import resource -import pytest import lib.fixtures -from lib import topolog -from lib.micronet import Commander, proc_error -from lib.micronet_cli import cli -from lib.micronet_compat import Mininet, cleanup_current, cleanup_previous +import pytest +from lib.micronet_compat import Mininet from lib.topogen import diagnose_env, get_topogen -from lib.topolog import logger -from lib.topotest import g_extra_config as topotest_extra_config +from lib.topolog import get_test_logdir, logger from lib.topotest import json_cmp_result +from munet import cli +from munet.base import Commander, proc_error +from munet.cleanup import cleanup_current, cleanup_previous +from munet.config import ConfigOptionsProxy +from munet.testing.util import pause_test + +from lib import topolog, topotest + +try: + # Used by munet native tests + from munet.testing.fixtures import event_loop, unet # pylint: disable=all # noqa + + @pytest.fixture(scope="module") + def rundir_module(pytestconfig): + d = os.path.join(pytestconfig.option.rundir, get_test_logdir()) + logging.debug("rundir_module: test module rundir %s", d) + return d + +except (AttributeError, ImportError): + pass def pytest_addoption(parser): @@ -60,12 +77,28 @@ def pytest_addoption(parser): ) parser.addoption( + "--logd", + action="append", + metavar="DAEMON[,ROUTER[,...]", + help=( + "Tail-F the DAEMON log file on all or a subset of ROUTERs." + " Option can be given multiple times." + ), + ) + + parser.addoption( "--pause", action="store_true", help="Pause after each test", ) parser.addoption( + "--pause-at-end", + action="store_true", + help="Pause before taking munet down", + ) + + parser.addoption( "--pause-on-error", action="store_true", help="Do not pause after (disables default when --shell or -vtysh given)", @@ -78,6 +111,30 @@ def pytest_addoption(parser): help="Do not pause after (disables default when --shell or -vtysh given)", ) + parser.addoption( + "--pcap", + default="", + metavar="NET[,NET...]", + help="Comma-separated list of networks to capture packets on, or 'all'", + ) + + parser.addoption( + "--perf", + action="append", + metavar="DAEMON[,ROUTER[,...]", + help=( + "Collect performance data from given DAEMON on all or a subset of ROUTERs." + " Option can be given multiple times." + ), + ) + + parser.addoption( + "--perf-options", + metavar="OPTS", + default="-g", + help="Options to pass to `perf record`.", + ) + rundir_help = "directory for running in and log files" parser.addini("rundir", rundir_help, default="/tmp/topotests") parser.addoption("--rundir", metavar="DIR", help=rundir_help) @@ -133,7 +190,7 @@ def pytest_addoption(parser): def check_for_memleaks(): - assert topotest_extra_config["valgrind_memleaks"] + assert topotest.g_pytest_config.option.valgrind_memleaks leaks = [] tgen = get_topogen() # pylint: disable=redefined-outer-name @@ -177,16 +234,15 @@ def check_for_memleaks(): @pytest.fixture(autouse=True, scope="module") def module_check_memtest(request): - del request # disable unused warning yield - if topotest_extra_config["valgrind_memleaks"]: + if request.config.option.valgrind_memleaks: if get_topogen() is not None: check_for_memleaks() def pytest_runtest_logstart(nodeid, location): # location is (filename, lineno, testname) - topolog.logstart(nodeid, location, topotest_extra_config["rundir"]) + topolog.logstart(nodeid, location, topotest.g_pytest_config.option.rundir) def pytest_runtest_logfinish(nodeid, location): @@ -197,10 +253,9 @@ def pytest_runtest_logfinish(nodeid, location): @pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(item: pytest.Item) -> None: "Hook the function that is called to execute the test." - del item # disable unused warning # For topology only run the CLI then exit - if topotest_extra_config["topology_only"]: + if item.config.option.topology_only: get_topogen().cli() pytest.exit("exiting after --topology-only") @@ -208,7 +263,7 @@ def pytest_runtest_call(item: pytest.Item) -> None: yield # Check for leaks if requested - if topotest_extra_config["valgrind_memleaks"]: + if item.config.option.valgrind_memleaks: check_for_memleaks() @@ -231,6 +286,7 @@ def pytest_configure(config): """ Assert that the environment is correctly configured, and get extra config. """ + topotest.g_pytest_config = ConfigOptionsProxy(config) if config.getoption("--collect-only"): return @@ -252,11 +308,13 @@ def pytest_configure(config): # Set some defaults for the pytest.ini [pytest] section # --------------------------------------------------- - rundir = config.getoption("--rundir") + rundir = config.option.rundir if not rundir: rundir = config.getini("rundir") if not rundir: rundir = "/tmp/topotests" + config.option.rundir = rundir + if not config.getoption("--junitxml"): config.option.xmlpath = os.path.join(rundir, "topotests.xml") xmlpath = config.option.xmlpath @@ -269,8 +327,6 @@ def pytest_configure(config): mv_path = commander.get_exec_path("mv") commander.cmd_status([mv_path, xmlpath, xmlpath + suffix]) - topotest_extra_config["rundir"] = rundir - # Set the log_file (exec) to inside the rundir if not specified if not config.getoption("--log-file") and not config.getini("log_file"): config.option.log_file = os.path.join(rundir, "exec.log") @@ -300,69 +356,19 @@ def pytest_configure(config): elif b and not is_xdist and not have_windows: pytest.exit("{} use requires byobu/TMUX/SCREEN/XTerm".format(feature)) - # --------------------------------------- - # Record our options in global dictionary - # --------------------------------------- - - topotest_extra_config["rundir"] = rundir - - asan_abort = config.getoption("--asan-abort") - topotest_extra_config["asan_abort"] = asan_abort - - gdb_routers = config.getoption("--gdb-routers") - gdb_routers = gdb_routers.split(",") if gdb_routers else [] - topotest_extra_config["gdb_routers"] = gdb_routers - - gdb_daemons = config.getoption("--gdb-daemons") - gdb_daemons = gdb_daemons.split(",") if gdb_daemons else [] - topotest_extra_config["gdb_daemons"] = gdb_daemons - assert_feature_windows(gdb_routers or gdb_daemons, "GDB") - - gdb_breakpoints = config.getoption("--gdb-breakpoints") - gdb_breakpoints = gdb_breakpoints.split(",") if gdb_breakpoints else [] - topotest_extra_config["gdb_breakpoints"] = gdb_breakpoints - - cli_on_error = config.getoption("--cli-on-error") - topotest_extra_config["cli_on_error"] = cli_on_error - assert_feature_windows(cli_on_error, "--cli-on-error") - - shell = config.getoption("--shell") - topotest_extra_config["shell"] = shell.split(",") if shell else [] - assert_feature_windows(shell, "--shell") - - strace = config.getoption("--strace-daemons") - topotest_extra_config["strace_daemons"] = strace.split(",") if strace else [] - - shell_on_error = config.getoption("--shell-on-error") - topotest_extra_config["shell_on_error"] = shell_on_error - assert_feature_windows(shell_on_error, "--shell-on-error") - - topotest_extra_config["valgrind_extra"] = config.getoption("--valgrind-extra") - topotest_extra_config["valgrind_memleaks"] = config.getoption("--valgrind-memleaks") - - vtysh = config.getoption("--vtysh") - topotest_extra_config["vtysh"] = vtysh.split(",") if vtysh else [] - assert_feature_windows(vtysh, "--vtysh") - - vtysh_on_error = config.getoption("--vtysh-on-error") - topotest_extra_config["vtysh_on_error"] = vtysh_on_error - assert_feature_windows(vtysh_on_error, "--vtysh-on-error") - - pause_on_error = vtysh or shell or config.getoption("--pause-on-error") - if config.getoption("--no-pause-on-error"): - pause_on_error = False - - topotest_extra_config["pause_on_error"] = pause_on_error - assert_feature_windows(pause_on_error, "--pause-on-error") - - pause = config.getoption("--pause") - topotest_extra_config["pause"] = pause - assert_feature_windows(pause, "--pause") - - topology_only = config.getoption("--topology-only") - if topology_only and is_xdist: + # + # Check for window capability if given options that require window + # + assert_feature_windows(config.option.gdb_routers, "GDB") + assert_feature_windows(config.option.gdb_daemons, "GDB") + assert_feature_windows(config.option.cli_on_error, "--cli-on-error") + assert_feature_windows(config.option.shell, "--shell") + assert_feature_windows(config.option.shell_on_error, "--shell-on-error") + assert_feature_windows(config.option.vtysh, "--vtysh") + assert_feature_windows(config.option.vtysh_on_error, "--vtysh-on-error") + + if config.option.topology_only and is_xdist: pytest.exit("Cannot use --topology-only with distributed test mode") - topotest_extra_config["topology_only"] = topology_only # Check environment now that we have config if not diagnose_env(rundir): @@ -428,10 +434,7 @@ def pytest_runtest_makereport(item, call): # We want to pause, if requested, on any error not just test cases # (e.g., call.when == "setup") if not pause: - pause = ( - topotest_extra_config["pause_on_error"] - or topotest_extra_config["pause"] - ) + pause = item.config.option.pause_on_error or item.config.option.pause # (topogen) Set topology error to avoid advancing in the test. tgen = get_topogen() # pylint: disable=redefined-outer-name @@ -443,9 +446,9 @@ def pytest_runtest_makereport(item, call): isatty = sys.stdout.isatty() error_cmd = None - if error and topotest_extra_config["vtysh_on_error"]: + if error and item.config.option.vtysh_on_error: error_cmd = commander.get_exec_path(["vtysh"]) - elif error and topotest_extra_config["shell_on_error"]: + elif error and item.config.option.shell_on_error: error_cmd = os.getenv("SHELL", commander.get_exec_path(["bash"])) if error_cmd: @@ -497,31 +500,16 @@ def pytest_runtest_makereport(item, call): if p.wait(): logger.warning("xterm proc failed: %s:", proc_error(p, o, e)) - if error and topotest_extra_config["cli_on_error"]: + if error and item.config.option.cli_on_error: # Really would like something better than using this global here. # Not all tests use topogen though so get_topogen() won't work. if Mininet.g_mnet_inst: - cli(Mininet.g_mnet_inst, title=title, background=False) + cli.cli(Mininet.g_mnet_inst, title=title, background=False) else: logger.error("Could not launch CLI b/c no mininet exists yet") - while pause and isatty: - try: - user = raw_input( - 'PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ' - ) - except NameError: - user = input('PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ') - user = user.strip() - - if user == "cli": - cli(Mininet.g_mnet_inst) - elif user == "pdb": - pdb.set_trace() # pylint: disable=forgotten-debug-statement - elif user: - print('Unrecognized input: "%s"' % user) - else: - break + if pause and isatty: + pause_test() # diff --git a/tests/topotests/cspf_topo1/reference/sharp-ted.json b/tests/topotests/cspf_topo1/reference/sharp-ted.json index da240e87a3..359b655f01 100644 --- a/tests/topotests/cspf_topo1/reference/sharp-ted.json +++ b/tests/topotests/cspf_topo1/reference/sharp-ted.json @@ -53,7 +53,7 @@ ], "edges":[ { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -96,7 +96,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -139,7 +139,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -182,7 +182,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -226,7 +226,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -275,7 +275,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -318,7 +318,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -362,7 +362,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -405,7 +405,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -448,7 +448,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -491,7 +491,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -534,7 +534,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -578,7 +578,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -622,7 +622,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/example_munet/munet.yaml b/tests/topotests/example_munet/munet.yaml new file mode 100644 index 0000000000..34e1470103 --- /dev/null +++ b/tests/topotests/example_munet/munet.yaml @@ -0,0 +1,17 @@ +version: 1 +topology: + ipv6-enable: true + networks-autonumber: true + networks: + - name: net1 + - name: net2 + nodes: + - name: r1 + kind: frr + connections: ["net1"] + - name: r2 + kind: frr + connections: ["net1", "net2"] + - name: r3 + kind: frr + connections: ["net2"] diff --git a/tests/topotests/example_munet/r1/daemons b/tests/topotests/example_munet/r1/daemons new file mode 100644 index 0000000000..a454c95923 --- /dev/null +++ b/tests/topotests/example_munet/r1/daemons @@ -0,0 +1,6 @@ +zebra=1 +staticd=1 +vtysh_enable=1 +watchfrr_enable=1 +zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log" +staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log" diff --git a/tests/topotests/example_munet/r1/frr.conf b/tests/topotests/example_munet/r1/frr.conf new file mode 100644 index 0000000000..468bda5e01 --- /dev/null +++ b/tests/topotests/example_munet/r1/frr.conf @@ -0,0 +1,7 @@ +log file /var/log/frr/frr.log +service integrated-vtysh-config + +interface eth0 + ip address 10.0.1.1/24 + +ip route 10.0.0.0/8 blackhole diff --git a/tests/topotests/example_munet/r1/vtysh.conf b/tests/topotests/example_munet/r1/vtysh.conf new file mode 100644 index 0000000000..f863f560f1 --- /dev/null +++ b/tests/topotests/example_munet/r1/vtysh.conf @@ -0,0 +1 @@ +service integrated-vtysh-config
\ No newline at end of file diff --git a/tests/topotests/example_munet/r2/daemons b/tests/topotests/example_munet/r2/daemons new file mode 100644 index 0000000000..a454c95923 --- /dev/null +++ b/tests/topotests/example_munet/r2/daemons @@ -0,0 +1,6 @@ +zebra=1 +staticd=1 +vtysh_enable=1 +watchfrr_enable=1 +zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log" +staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log" diff --git a/tests/topotests/example_munet/r2/frr.conf b/tests/topotests/example_munet/r2/frr.conf new file mode 100644 index 0000000000..77d9892485 --- /dev/null +++ b/tests/topotests/example_munet/r2/frr.conf @@ -0,0 +1,10 @@ +log file /var/log/frr/frr.log +service integrated-vtysh-config + +interface eth0 + ip address 10.0.1.2/24 + +interface eth1 + ip address 10.0.2.2/24 + +ip route 10.0.0.0/8 blackhole diff --git a/tests/topotests/example_munet/r2/vtysh.conf b/tests/topotests/example_munet/r2/vtysh.conf new file mode 100644 index 0000000000..f863f560f1 --- /dev/null +++ b/tests/topotests/example_munet/r2/vtysh.conf @@ -0,0 +1 @@ +service integrated-vtysh-config
\ No newline at end of file diff --git a/tests/topotests/example_munet/r3/daemons b/tests/topotests/example_munet/r3/daemons new file mode 100644 index 0000000000..a454c95923 --- /dev/null +++ b/tests/topotests/example_munet/r3/daemons @@ -0,0 +1,6 @@ +zebra=1 +staticd=1 +vtysh_enable=1 +watchfrr_enable=1 +zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log" +staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log" diff --git a/tests/topotests/example_munet/r3/frr.conf b/tests/topotests/example_munet/r3/frr.conf new file mode 100644 index 0000000000..e0839e6d8a --- /dev/null +++ b/tests/topotests/example_munet/r3/frr.conf @@ -0,0 +1,7 @@ +log file /var/log/frr/frr.log +service integrated-vtysh-config + +interface eth0 + ip address 10.0.2.3/24 + +ip route 10.0.0.0/8 blackhole diff --git a/tests/topotests/example_munet/r3/vtysh.conf b/tests/topotests/example_munet/r3/vtysh.conf new file mode 100644 index 0000000000..f863f560f1 --- /dev/null +++ b/tests/topotests/example_munet/r3/vtysh.conf @@ -0,0 +1 @@ +service integrated-vtysh-config
\ No newline at end of file diff --git a/tests/topotests/example_munet/test_munet.py b/tests/topotests/example_munet/test_munet.py new file mode 100644 index 0000000000..0d9599fa54 --- /dev/null +++ b/tests/topotests/example_munet/test_munet.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 23 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +async def test_native_test(unet): + o = unet.hosts["r1"].cmd_nostatus("ip addr") + print(o) diff --git a/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py b/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py index 5eef879e3f..ada8c0f5fb 100644 --- a/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py +++ b/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py @@ -121,14 +121,14 @@ def _check_interface_metrics(router, expected_metrics): tgen = get_topogen() router = tgen.gears[router] logger.info(f"check_interface_metrics {router}") - isis_interface_output = router.vtysh_cmd( - "show isis interface detail json" - ) + isis_interface_output = router.vtysh_cmd("show isis interface detail json") intf_json = json.loads(isis_interface_output) for i in range(len(expected_metrics)): - metric = intf_json["areas"][0]["circuits"][i]["interface"]["levels"][0]["metric"] - if (metric != expected_metrics[i]): + metric = intf_json["areas"][0]["circuits"][i]["interface"]["levels"][0][ + "metric" + ] + if metric != expected_metrics[i]: intf_name = intf_json["areas"][0]["circuits"][i]["interface"]["name"] return "{} with expected metric {} on {} got {}".format( router.name, expected_metrics[i], intf_name, metric @@ -139,9 +139,7 @@ def _check_interface_metrics(router, expected_metrics): def check_interface_metrics(router, expected_metrics): "Verfiy metrics on router's isis interfaces" - assertmsg = _check_interface_metrics( - router, expected_metrics - ) + assertmsg = _check_interface_metrics(router, expected_metrics) assert assertmsg is True, assertmsg @@ -151,9 +149,7 @@ def _check_lsp_metrics(router, lsp, expected_metrics): tgen = get_topogen() router = tgen.gears[router] logger.info(f"check_lsp_metrics {router}") - isis_lsp_output = router.vtysh_cmd( - "show isis database detail {}".format(lsp) - ) + isis_lsp_output = router.vtysh_cmd("show isis database detail {}".format(lsp)) metrics_list = [int(i) for i in re.findall(r"Metric: (\d+)", isis_lsp_output)] if len(metrics_list) == 0: @@ -170,9 +166,7 @@ def _check_lsp_metrics(router, lsp, expected_metrics): def check_lsp_metrics(router, lsp, expected_metrics): "Verfiy metrics on router's lsp" - assertmsg = _check_lsp_metrics( - router, lsp, expected_metrics - ) + assertmsg = _check_lsp_metrics(router, lsp, expected_metrics) assert assertmsg is True, assertmsg @@ -183,14 +177,12 @@ def _check_ip_route(router, destination, expected_interface): tgen = get_topogen() router = tgen.gears[router] logger.info(f"check_ip_route {router}") - route_output = router.vtysh_cmd( - "show ip route {} json".format(destination) - ) + route_output = router.vtysh_cmd("show ip route {} json".format(destination)) route_json = json.loads(route_output) interface = route_json[destination][0]["nexthops"][0]["interfaceName"] - if (interface != expected_interface): + if interface != expected_interface: return "{} with expected route to {} got {} expected {}".format( router.name, destination, interface, expected_interface ) @@ -201,9 +193,7 @@ def _check_ip_route(router, destination, expected_interface): def check_ip_route(router, destination, expected_interface): "Verfiy IS-IS route" - assertmsg = _check_ip_route( - router, destination, expected_interface - ) + assertmsg = _check_ip_route(router, destination, expected_interface) assert assertmsg is True, assertmsg @@ -216,9 +206,7 @@ def test_isis_daemon_up(): for router in ["r1", "r2", "r3", "r4"]: r = tgen.gears[router] - daemons = r.vtysh_cmd( - "show daemons" - ) + daemons = r.vtysh_cmd("show daemons") assert "isisd" in daemons # Verify initial metric values. @@ -420,9 +408,9 @@ def test_isis_advertise_high_metrics_route(): Topology: r2 - / \ + // \\ r1 r4 - \ / + \\ // r3 Devices are configured with preferred route between r1 and r4: diff --git a/tests/topotests/isis_snmp/r5/ldpdconf b/tests/topotests/isis_snmp/r5/ldpdconf index fc700608b5..b3d10b07ec 100644 --- a/tests/topotests/isis_snmp/r5/ldpdconf +++ b/tests/topotests/isis_snmp/r5/ldpdconf @@ -1,10 +1,10 @@ hostname r5 log file ldpd.log ! -debug mpls ldp zebra -debug mpls ldp event -debug mpls ldp errors -debug mpls ldp sync +!debug mpls ldp zebra +!debug mpls ldp event +!debug mpls ldp errors +!debug mpls ldp sync ! mpls ldp router-id 3.3.3.3 diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf new file mode 100644 index 0000000000..5503baa58c --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf @@ -0,0 +1,96 @@ +password 1 +hostname rt1 +log file isisd.log +! +!debug northbound +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +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-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0001.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 201 + dataplane sr-mpls + advertise-definition + affinity exclude-any red + ! + flex-algo 202 + dataplane sr-mpls + advertise-definition + affinity exclude-any blue + ! + flex-algo 203 + dataplane sr-mpls + advertise-definition + affinity exclude-any green + ! + flex-algo 204 + dataplane sr-mpls + advertise-definition + affinity include-any blue green + ! + flex-algo 205 + dataplane sr-mpls + advertise-definition + affinity include-any red green + ! + flex-algo 206 + dataplane sr-mpls + advertise-definition + affinity include-any red blue + ! + flex-algo 207 + dataplane sr-mpls + advertise-definition + affinity include-all yellow orange + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 1 + segment-routing prefix 1.1.1.1/32 algorithm 201 index 101 + segment-routing prefix 1.1.1.1/32 algorithm 202 index 201 + segment-routing prefix 1.1.1.1/32 algorithm 203 index 301 + segment-routing prefix 1.1.1.1/32 algorithm 204 index 401 + segment-routing prefix 1.1.1.1/32 algorithm 205 index 501 + segment-routing prefix 1.1.1.1/32 algorithm 206 index 601 + segment-routing prefix 1.1.1.1/32 algorithm 207 index 701 + segment-routing prefix 2001:db8:1000::1/128 index 1001 + segment-routing prefix 2001:db8:1000::1/128 algorithm 201 index 1101 + segment-routing prefix 2001:db8:1000::1/128 algorithm 202 index 1201 + segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301 + segment-routing prefix 2001:db8:1000::1/128 algorithm 204 index 1401 + segment-routing prefix 2001:db8:1000::1/128 algorithm 205 index 1501 + segment-routing prefix 2001:db8:1000::1/128 algorithm 206 index 1601 + segment-routing prefix 2001:db8:1000::1/128 algorithm 207 index 1701 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref new file mode 100644 index 0000000000..750abc1fa3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set
\ No newline at end of file diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref new file mode 100644 index 0000000000..13a96161af --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref @@ -0,0 +1,115 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: Not found + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref new file mode 100644 index 0000000000..d311e924d6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref new file mode 100644 index 0000000000..ce31766da5 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref @@ -0,0 +1,112 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: None + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref new file mode 100644 index 0000000000..d311e924d6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref new file mode 100644 index 0000000000..e56e5af2f9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref @@ -0,0 +1,108 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref new file mode 100644 index 0000000000..d311e924d6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf new file mode 100644 index 0000000000..5140eda73a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf @@ -0,0 +1,39 @@ +log file zebra.log +! +hostname rt1 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip address 1.1.1.1/32 + ipv6 address 2001:db8:1000::1/128 +! +interface eth-rt2 + ip address 10.12.0.1/24 + link-params + affinity red + exit-link-params +! +interface eth-rt3 + ip address 10.13.0.1/24 + link-params + affinity green yellow orange + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf new file mode 100644 index 0000000000..8655e7434a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf @@ -0,0 +1,96 @@ +password 1 +hostname rt2 +log file isisd.log +! +!debug northbound +!debug isis events +!debug isis route-events +!debug isis spf-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +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-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0002.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 201 + dataplane sr-mpls + advertise-definition + affinity exclude-any red + ! + flex-algo 202 + dataplane sr-mpls + advertise-definition + affinity exclude-any blue + ! + flex-algo 203 + dataplane sr-mpls + advertise-definition + affinity exclude-any green + ! + flex-algo 204 + dataplane sr-mpls + advertise-definition + affinity include-any blue green + ! + flex-algo 205 + dataplane sr-mpls + advertise-definition + affinity include-any red green + ! + flex-algo 206 + dataplane sr-mpls + advertise-definition + affinity include-any red blue + ! + flex-algo 207 + dataplane sr-mpls + advertise-definition + affinity include-all yellow orange + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 2 + segment-routing prefix 2.2.2.2/32 algorithm 201 index 102 + segment-routing prefix 2.2.2.2/32 algorithm 202 index 202 + segment-routing prefix 2.2.2.2/32 algorithm 203 index 302 + segment-routing prefix 2.2.2.2/32 algorithm 204 index 402 + segment-routing prefix 2.2.2.2/32 algorithm 205 index 502 + segment-routing prefix 2.2.2.2/32 algorithm 206 index 602 + segment-routing prefix 2.2.2.2/32 algorithm 207 index 702 + segment-routing prefix 2001:db8:1000::2/128 index 1002 + segment-routing prefix 2001:db8:1000::2/128 algorithm 201 index 1102 + segment-routing prefix 2001:db8:1000::2/128 algorithm 202 index 1202 + segment-routing prefix 2001:db8:1000::2/128 algorithm 203 index 1302 + segment-routing prefix 2001:db8:1000::2/128 algorithm 204 index 1402 + segment-routing prefix 2001:db8:1000::2/128 algorithm 205 index 1502 + segment-routing prefix 2001:db8:1000::2/128 algorithm 206 index 1602 + segment-routing prefix 2001:db8:1000::2/128 algorithm 207 index 1702 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref new file mode 100644 index 0000000000..0ed0eb31ee --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20311":{ + "inLabel":20311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21311":{ + "inLabel":21311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref new file mode 100644 index 0000000000..b10cb70866 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref @@ -0,0 +1,114 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: Not found + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref new file mode 100644 index 0000000000..8b407387b4 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref new file mode 100644 index 0000000000..358c4310ed --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref @@ -0,0 +1,111 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: None + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref new file mode 100644 index 0000000000..8b407387b4 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref new file mode 100644 index 0000000000..04d07e6414 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref @@ -0,0 +1,107 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref new file mode 100644 index 0000000000..8b407387b4 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref new file mode 100644 index 0000000000..3db0ebfbf6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref @@ -0,0 +1,482 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf new file mode 100644 index 0000000000..388348fced --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf @@ -0,0 +1,39 @@ +log file zebra.log +! +hostname rt2 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip address 2.2.2.2/32 + ipv6 address 2001:db8:1000::2/128 +! +interface eth-rt1 + ip address 10.12.0.2/24 + link-params + affinity red + exit-link-params +! +interface eth-rt3 + ip address 10.23.0.2/24 + link-params + affinity blue yellow orange + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf new file mode 100644 index 0000000000..d77af81d7c --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf @@ -0,0 +1,76 @@ +password 1 +hostname rt3 +log file isisd.log +! +!debug northbound +!debug isis events +!debug isis route-events +!debug isis spf-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +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-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0003.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 201 + dataplane sr-mpls + flex-algo 202 + dataplane sr-mpls + flex-algo 203 + dataplane sr-mpls + flex-algo 204 + dataplane sr-mpls + flex-algo 205 + dataplane sr-mpls + flex-algo 206 + dataplane sr-mpls + flex-algo 207 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 3 + segment-routing prefix 3.3.3.3/32 algorithm 201 index 103 + segment-routing prefix 3.3.3.3/32 algorithm 202 index 203 + segment-routing prefix 3.3.3.3/32 algorithm 203 index 303 + segment-routing prefix 3.3.3.3/32 algorithm 204 index 403 + segment-routing prefix 3.3.3.3/32 algorithm 205 index 503 + segment-routing prefix 3.3.3.3/32 algorithm 206 index 603 + segment-routing prefix 3.3.3.3/32 algorithm 207 index 703 + segment-routing prefix 2001:db8:1000::3/128 index 1003 + segment-routing prefix 2001:db8:1000::3/128 algorithm 201 index 1103 + segment-routing prefix 2001:db8:1000::3/128 algorithm 202 index 1203 + segment-routing prefix 2001:db8:1000::3/128 algorithm 203 index 1303 + segment-routing prefix 2001:db8:1000::3/128 algorithm 204 index 1403 + segment-routing prefix 2001:db8:1000::3/128 algorithm 205 index 1503 + segment-routing prefix 2001:db8:1000::3/128 algorithm 206 index 1603 + segment-routing prefix 2001:db8:1000::3/128 algorithm 207 index 1703 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref new file mode 100644 index 0000000000..7954734936 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set
\ No newline at end of file diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref new file mode 100644 index 0000000000..57755b00e6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20311":{ + "inLabel":20311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20311, + "outLabelStack":[ + 20311 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21311":{ + "inLabel":21311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21311, + "outLabelStack":[ + 21311 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref new file mode 100644 index 0000000000..2ccc4f1655 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref @@ -0,0 +1,114 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: Not found + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref new file mode 100644 index 0000000000..1b57f575b2 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref new file mode 100644 index 0000000000..903c0f2bed --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref @@ -0,0 +1,111 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: None + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref new file mode 100644 index 0000000000..1b57f575b2 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref new file mode 100644 index 0000000000..f36d96579d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref @@ -0,0 +1,107 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref new file mode 100644 index 0000000000..1b57f575b2 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref new file mode 100644 index 0000000000..8ae983a790 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref @@ -0,0 +1,482 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf new file mode 100644 index 0000000000..fb45ee1282 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf @@ -0,0 +1,39 @@ +log file zebra.log +! +hostname rt3 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip address 3.3.3.3/32 + ipv6 address 2001:db8:1000::3/128 +! +interface eth-rt1 + ip address 10.13.0.3/24 + link-params + affinity green yellow orange + exit-link-params +! +interface eth-rt2 + ip address 10.23.0.3/24 + link-params + affinity blue yellow orange + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py b/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py new file mode 100755 index 0000000000..85600beb0e --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py @@ -0,0 +1,583 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright 2021 by LINE Corporation, Hiroki Shirokura <hiroki.shirokura@linecorp.com> +# Copyright 2023 6WIND S.A. + +""" +test_isis_sr_flex_algo_topo1.py: + +[+] Flex-Algos 201 exclude red +[+] Flex-Algos 202 exclude blue +[+] Flex-Algos 203 exclude green +[+] Flex-Algos 204 include-any blue green +[+] Flex-Algos 205 include-any red green +[+] Flex-Algos 206 include-any red blue +[+] Flex-Algos 207 include-all yellow orange + + +--------+ 10.12.0.0/24 +--------+ + | | red | | + | RT1 |----------------| RT2 | + | | | | + +--------+ +--------+ + 10.13.0.0/24 \\ / 10.23.0.0/24 + green \\ / blue + yellow \\ / yellow + orange +--------+ orange + | | + | RT3 | + | | + +--------+ +""" + +import os +import sys +import pytest +import json +import tempfile +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 + + +pytestmark = [pytest.mark.isisd] + +# Global multi-dimensional dictionary containing all expected outputs +outputs = {} + + +def build_topo(tgen): + "Build function" + + def connect_routers(tgen, left_idx, right_idx): + left = "rt{}".format(left_idx) + right = "rt{}".format(right_idx) + switch = tgen.add_switch("s-{}-{}".format(left, right)) + switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right)) + switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left)) + l_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, left_idx) + tgen.gears[left].run("ip link set eth-{} down".format(right)) + tgen.gears[left].run("ip link set eth-{} address {}".format(right, l_addr)) + tgen.gears[left].run("ip link set eth-{} up".format(right)) + r_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, right_idx) + tgen.gears[right].run("ip link set eth-{} down".format(left)) + tgen.gears[right].run("ip link set eth-{} address {}".format(left, r_addr)) + tgen.gears[right].run("ip link set eth-{} up".format(left)) + + tgen.add_router("rt1") + tgen.add_router("rt2") + tgen.add_router("rt3") + connect_routers(tgen, 1, 2) + connect_routers(tgen, 2, 3) + connect_routers(tgen, 3, 1) + + # + # Populate multi-dimensional dictionary containing all expected outputs + # + number_of_steps = 11 + filenames = [ + "show_mpls_table.ref", + "show_isis_flex_algo.ref", + ] + for rname in ["rt1", "rt2", "rt3"]: + outputs[rname] = {} + for step in range(1, number_of_steps + 1): + outputs[rname][step] = {} + for filename in filenames: + # Get snapshots relative to the expected network convergence + filename_pullpath = "{}/{}/step{}/{}".format(CWD, rname, step, filename) + outputs[rname][step][filename] = open(filename_pullpath).read() + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, 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.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() + tgen.stop_topology() + + +def setup_testcase(msg): + logger.info(msg) + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + return tgen + + +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 + + +def router_compare_output(rname, command, reference): + "Compare router output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_output_cmp, tgen.gears[rname], command, reference) + result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5) + assertmsg = '{} command "{}" output mismatches the expected result:\n{}'.format(rname, command, diff) + assert result, assertmsg + + +# +# Step 1 +# +# Test initial network convergenece +# +# All flex-algo are defined and its fib entries are installed +# +def test_step1_mpls_lfib(): + logger.info("Test (step 1)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][1]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][1]["show_mpls_table.ref"]) + + +# +# Step 2 +# +# Action(s): +# - Disable flex-algo-203 definition advertisement on rt1 +# +# Expected change(s): +# - Nothing +# +# Description: +# No change occurs because it refers to the FAD set in rt2. +# +def test_step2_mpls_lfib(): + logger.info("Test (step 2)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + no advertise-definition + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][2]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][2]["show_mpls_table.ref"]) + + +# +# Step 3 +# +# Action(s): +# - Disable flex-algo-203 definition advertisement on rt2 +# +# Expected change(s): +# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203 +# +# Description: +# When all FADs are disappeared, all their prefix sid routes are withdrawn. +# +def test_step3_mpls_lfib(): + logger.info("Test (step 3)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt2"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + no advertise-definition + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][3]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][3]["show_mpls_table.ref"]) + + +# +# Step 4 +# +# Action(s): +# - Enable flex-algo-203 definition advertisement on rt2 +# +# Expected change(s): +# - rt1,rt2,rt3 should install all Prefix-SIDs of flex-algo-203 +# +# Description: +# Since the FAD is restored, the reachability to the Prefix-SID is restored. +# +def test_step4_mpls_lfib(): + logger.info("Test (step 4)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt2"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][4]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][4]["show_mpls_table.ref"]) + + +# +# Step 5 +# +# Action(s): +# - Enable flex-algo-203 definition advertisement on rt1 +# +# Expected change(s): +# - Nothing +# +# Description: +# This does not affect the FIB, since there is already a FAD for rt2. +# However, the FAD owner will be changed from rt2 to rt1. +# +def test_step5_mpls_lfib(): + logger.info("Test (step 5)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][5]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][5]["show_mpls_table.ref"]) + + +# +# Step 6 +# +# Action(s): +# - Disable flex-algo-203 SR-MPLS dataplane on rt1 +# - Disable flex-algo-203 SR-MPLS dataplane on rt2 +# - Disable flex-algo-203 SR-MPLS dataplane on rt3 +# +# Expected change(s): +# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203 +# +# Description: +# Clear the Flex-Algo 203 whole settings on each routers. All routes related +# to it will be withdrawn. +# +def test_step6_mpls_lfib(): + logger.info("Test (step 6)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1", "rt2", "rt3"]: + tgen.gears[rname].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + no dataplane sr-mpls + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][6]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][6]["show_mpls_table.ref"]) + + +# +# Step 7 +# +# Action(s): +# - Disable flex-algo-203 all configuration on rt1 +# - Disable flex-algo-203 all configuration on rt2 +# - Disable flex-algo-203 all configuration on rt3 +# +# Expected change(s): +# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203 +# +# Description: +# Clear the Flex-Algo 203 whole settings on each routers. All routes related +# to it will be withdrawn. +# +def test_step7_mpls_lfib(): + logger.info("Test (step 7)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1", "rt2", "rt3"]: + tgen.gears[rname].vtysh_cmd( + """ + configure terminal + router isis 1 + no flex-algo 203 + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][7]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][7]["show_mpls_table.ref"]) + +# +# Step 8 +# +# Action(s): +# - Enable flex-algo-203 all configuration on rt1 +# - Enable flex-algo-203 all configuration on rt2 +# - Enable flex-algo-203 all configuration on rt3 +# +# Expected change(s): +# - rt1,rt2,rt3 should install all Prefix-SIDs of flex-algo-203 +# +# Description: +# All configurations were backed. +# +def test_step8_mpls_lfib(): + logger.info("Test (step 8)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + affinity exclude-any green + dataplane sr-mpls + """) + + tgen.gears["rt2"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + affinity exclude-any green + dataplane sr-mpls + """) + + tgen.gears["rt3"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + dataplane sr-mpls + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][8]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][8]["show_mpls_table.ref"]) + + +# +# Step 9 +# +# Action(s): +# - Disable algorithm prefix-sid of algo-203 on rt1 +# +# Expected change(s): +# - rt1 should uninstall all Prefix-SIDs of flex-algo-203 +# - rt2 should uninstall Prefix-SIDs of rt1's flex-algo-203 +# - rt3 should uninstall Prefix-SIDs of rt1's flex-algo-203 +# +def test_step9_mpls_lfib(): + logger.info("Test (step 9)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + no segment-routing prefix 1.1.1.1/32 algorithm 203 index 301 + no segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301 + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][9]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][9]["show_mpls_table.ref"]) + + +# +# Step 10 +# +# Action(s): +# - Enable algorithm prefix-sid of algo-203 on rt1 +# +# Expected change(s): +# - rt1 should install all Prefix-SIDs of flex-algo-203 +# - rt2 should install Prefix-SIDs of rt1's flex-algo-203 +# - rt3 should install Prefix-SIDs of rt1's flex-algo-203 +# +def test_step10_mpls_lfib(): + logger.info("Test (step 10)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + segment-routing prefix 1.1.1.1/32 algorithm 203 index 301 + segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301 + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][10]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][10]["show_mpls_table.ref"]) + + +# +# Step 11 +# +# Action(s): +# - Update algorithm prefix-sid of algo-203 on rt1 from 301 to 311 +# +# Expected change(s): +# - rt2 should update Prefix-SIDs of rt1's flex-algo-203 from 301 to 311 +# - rt3 should update Prefix-SIDs of rt1's flex-algo-203 from 301 to 311 +# +def test_step11_mpls_lfib(): + logger.info("Test (step 11)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + segment-routing prefix 1.1.1.1/32 algorithm 203 index 311 + segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1311 + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][11]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][11]["show_mpls_table.ref"]) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis_sr_flex_algo_topo2/README.md b/tests/topotests/isis_sr_flex_algo_topo2/README.md new file mode 100644 index 0000000000..20282c49ee --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/README.md @@ -0,0 +1,8 @@ + +## test + +``` +fdk-enter rt9.pid iperf3 -s +fdk-enter rt0.pid iperf3 -B 111.111.111.111 -c 222.222.222.222 -P20 -t 100000 +fdk-enter rt0.pid watch -n0.1 ip -s link show +``` diff --git a/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh b/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh new file mode 100755 index 0000000000..527f05b60b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +if [ $# -ne 1 ]; then + echo "invalid command syntax" 1>&2 + echo "Usage: $0 <0|128|129|130>" 1>&2 + exit 1 +fi + +case "$1" in + 0 ) echo ;; + 128 ) echo ;; + 129 ) echo ;; + 130 ) echo ;; + * ) echo "error" ; exit ;; +esac + +R0=$(cat /tmp/topotests/isis_sr_flex_algo_topo2.test_isis_sr_flex_algo_topo2/rt0.pid) +R9=$(cat /tmp/topotests/isis_sr_flex_algo_topo2.test_isis_sr_flex_algo_topo2/rt9.pid) + +set -x + +cat <<EOF | nsenter -a -t $R0 vtysh +conf te +segment-routing + traffic-eng + policy color 1 endpoint 9.9.9.9 + name sid-algorithm + binding-sid 111 + candidate-path preference 100 name sid-algorithm explicit segment-list sid-algorithm-$1 + exit + exit +exit +EOF + +cat <<EOF | nsenter -a -t $R9 vtysh +conf te +segment-routing + traffic-eng + policy color 1 endpoint 10.10.10.10 + name sid-algorithm + binding-sid 222 + candidate-path preference 100 name sid-algorithm explicit segment-list sid-algorithm-$1 + exit + exit +exit +EOF diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf new file mode 100644 index 0000000000..3915bec385 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 1 + bgp router-id 10.10.10.10 + no bgp network import-check + neighbor 9.9.9.9 remote-as 1 + neighbor 9.9.9.9 update-source 10.10.10.10 + ! + address-family ipv4 unicast + network 10.255.0.0/24 + neighbor 9.9.9.9 next-hop-self + neighbor 9.9.9.9 route-map sr-te in + exit-address-family +! +route-map sr-te permit 10 + set sr-te color 1 +exit +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf new file mode 100644 index 0000000000..cbf25504ef --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf @@ -0,0 +1,56 @@ +password 1 +hostname rt0 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1000.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + advertise-definition + ! + flex-algo 129 + dataplane sr-mpls + advertise-definition + ! + flex-algo 130 + dataplane sr-mpls + advertise-definition + affinity include-any blue + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 10.10.10.10/32 index 0 + segment-routing prefix 10.10.10.10/32 algorithm 128 index 100 + segment-routing prefix 10.10.10.10/32 algorithm 129 index 200 + segment-routing prefix 10.10.10.10/32 algorithm 130 index 300 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf new file mode 100644 index 0000000000..c51b4e0247 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf @@ -0,0 +1,20 @@ +log file pathd.log +! +hostname rt0 +! +segment-routing + traffic-eng + segment-list sid-algorithm-0 + index 10 mpls label 20009 + exit + segment-list sid-algorithm-128 + index 10 mpls label 20109 + exit + segment-list sid-algorithm-129 + index 10 mpls label 20209 + exit + segment-list sid-algorithm-130 + index 10 mpls label 20309 + exit + exit +exit diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json new file mode 100644 index 0000000000..51a1c25556 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json @@ -0,0 +1,438 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20103, + "outLabelStack":[ + 20103 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20207, + "outLabelStack":[ + 20207 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf new file mode 100644 index 0000000000..89837d4cf5 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf @@ -0,0 +1,34 @@ +log file zebra.log +! +hostname rt0 +! +!log stdout notifications +!log monitor notifications +!log commands +! +debug zebra packet +debug zebra dplane +debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 10.10.10.10/32 +! +interface eth-rt1 + ip address 10.1.0.10/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt5 + ip address 10.5.0.10/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf new file mode 100644 index 0000000000..b6bba0c1c3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf @@ -0,0 +1,60 @@ +password 1 +hostname rt1 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt0 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt2 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt4 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1001.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 1 + segment-routing prefix 1.1.1.1/32 algorithm 128 index 101 + segment-routing prefix 1.1.1.1/32 algorithm 129 index 201 + segment-routing prefix 1.1.1.1/32 algorithm 130 index 301 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json new file mode 100644 index 0000000000..50066250b8 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20103, + "outLabelStack":[ + 20103 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20103, + "outLabelStack":[ + 20103 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf new file mode 100644 index 0000000000..25a96290ae --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt1 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 1.1.1.1/32 +! +interface eth-rt0 + ip address 10.1.0.1/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt2 + ip address 10.12.0.1/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt4 + ip address 10.14.0.1/24 +! +interface eth-rt5 + ip address 10.15.0.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf new file mode 100644 index 0000000000..f051a68e21 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf @@ -0,0 +1,54 @@ +password 1 +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 +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt6 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1002.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 2 + segment-routing prefix 2.2.2.2/32 algorithm 128 index 102 + segment-routing prefix 2.2.2.2/32 algorithm 129 index 202 + segment-routing prefix 2.2.2.2/32 algorithm 130 index 302 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json new file mode 100644 index 0000000000..679b41db03 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json @@ -0,0 +1,408 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf new file mode 100644 index 0000000000..d739a732a9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf @@ -0,0 +1,37 @@ +log file zebra.log +! +hostname rt2 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 2.2.2.2/32 +! +interface eth-rt1 + ip address 10.12.0.2/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt3 + ip address 10.23.0.2/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt6 + ip address 10.26.0.2/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf new file mode 100644 index 0000000000..644e656bfd --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf @@ -0,0 +1,60 @@ +password 1 +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 +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt4 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt9 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1003.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 3 + segment-routing prefix 3.3.3.3/32 algorithm 128 index 103 + segment-routing prefix 3.3.3.3/32 algorithm 129 index 203 + segment-routing prefix 3.3.3.3/32 algorithm 130 index 303 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json new file mode 100644 index 0000000000..f930faa61f --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + }, + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf new file mode 100644 index 0000000000..5c3bed0763 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt3 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 3.3.3.3/32 +! +interface eth-rt2 + ip address 10.23.0.3/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt4 + ip address 10.34.0.3/24 +! +interface eth-rt7 + ip address 10.37.0.3/24 +! +interface eth-rt9 + ip address 10.39.0.3/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf new file mode 100644 index 0000000000..1ab200fbd8 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf @@ -0,0 +1,52 @@ +password 1 +hostname rt4 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt8 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1004.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 4.4.4.4/32 index 4 + segment-routing prefix 4.4.4.4/32 algorithm 128 index 104 + segment-routing prefix 4.4.4.4/32 algorithm 129 index 204 + segment-routing prefix 4.4.4.4/32 algorithm 130 index 304 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json new file mode 100644 index 0000000000..141e40d455 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json @@ -0,0 +1,286 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf new file mode 100644 index 0000000000..9c00013e70 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf @@ -0,0 +1,31 @@ +log file zebra.log +! +hostname rt4 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 4.4.4.4/32 +! +interface eth-rt1 + ip address 10.14.0.4/24 +! +interface eth-rt3 + ip address 10.34.0.4/24 +! +interface eth-rt8 + ip address 10.48.0.4/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf new file mode 100644 index 0000000000..54cc37711e --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf @@ -0,0 +1,60 @@ +password 1 +hostname rt5 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt0 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt6 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt8 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1005.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 5.5.5.5/32 index 5 + segment-routing prefix 5.5.5.5/32 algorithm 128 index 105 + segment-routing prefix 5.5.5.5/32 algorithm 129 index 205 + segment-routing prefix 5.5.5.5/32 algorithm 130 index 305 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json new file mode 100644 index 0000000000..82ebfc075f --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20207, + "outLabelStack":[ + 20207 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20207, + "outLabelStack":[ + 20207 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf new file mode 100644 index 0000000000..61c599db7b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt5 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 5.5.5.5/32 +! +interface eth-rt0 + ip address 10.5.0.5/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt1 + ip address 10.15.0.5/24 +! +interface eth-rt6 + ip address 10.56.0.5/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt8 + ip address 10.58.0.5/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf new file mode 100644 index 0000000000..fc8660cfa6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf @@ -0,0 +1,54 @@ +password 1 +hostname rt6 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1006.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 6.6.6.6/32 index 6 + segment-routing prefix 6.6.6.6/32 algorithm 128 index 106 + segment-routing prefix 6.6.6.6/32 algorithm 129 index 206 + segment-routing prefix 6.6.6.6/32 algorithm 130 index 306 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json new file mode 100644 index 0000000000..2cc7277b41 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json @@ -0,0 +1,408 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf new file mode 100644 index 0000000000..b63401e114 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf @@ -0,0 +1,37 @@ +log file zebra.log +! +hostname rt6 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 6.6.6.6/32 +! +interface eth-rt2 + ip address 10.26.0.6/24 +! +interface eth-rt5 + ip address 10.56.0.6/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt7 + ip address 10.67.0.6/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf new file mode 100644 index 0000000000..10dc9812a4 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf @@ -0,0 +1,60 @@ +password 1 +hostname rt7 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt6 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt8 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt9 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1007.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 7.7.7.7/32 index 7 + segment-routing prefix 7.7.7.7/32 algorithm 128 index 107 + segment-routing prefix 7.7.7.7/32 algorithm 129 index 207 + segment-routing prefix 7.7.7.7/32 algorithm 130 index 307 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json new file mode 100644 index 0000000000..aeaa6046ab --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20205, + "outLabelStack":[ + 20205 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20205, + "outLabelStack":[ + 20205 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + }, + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf new file mode 100644 index 0000000000..b5a28c7f1a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt7 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 7.7.7.7/32 +! +interface eth-rt3 + ip address 10.37.0.7/24 +! +interface eth-rt6 + ip address 10.67.0.7/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt8 + ip address 10.78.0.7/24 +! +interface eth-rt9 + ip address 10.79.0.7/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf new file mode 100644 index 0000000000..4ca45a8ad9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf @@ -0,0 +1,50 @@ +password 1 +hostname rt8 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt4 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1008.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 8.8.8.8/32 index 8 + segment-routing prefix 8.8.8.8/32 algorithm 128 index 108 + segment-routing prefix 8.8.8.8/32 algorithm 129 index 208 + segment-routing prefix 8.8.8.8/32 algorithm 130 index 308 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json new file mode 100644 index 0000000000..27470b76cc --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json @@ -0,0 +1,286 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf new file mode 100644 index 0000000000..dd63f8cc2f --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf @@ -0,0 +1,29 @@ +log file zebra.log +! +hostname rt8 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface lo + ip address 8.8.8.8/32 +! +interface eth-rt4 + ip address 10.48.0.8/24 +! +interface eth-rt5 + ip address 10.58.0.8/24 +! +interface eth-rt7 + ip address 10.78.0.8/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf new file mode 100644 index 0000000000..386d8118e7 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 1 + bgp router-id 9.9.9.9 + no bgp network import-check + neighbor 10.10.10.10 remote-as 1 + neighbor 10.10.10.10 update-source 9.9.9.9 + ! + address-family ipv4 unicast + network 10.255.9.0/24 + neighbor 10.10.10.10 next-hop-self + neighbor 10.10.10.10 route-map sr-te in + exit-address-family +! +route-map sr-te permit 10 + set sr-te color 1 +exit +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf new file mode 100644 index 0000000000..89eab274c7 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf @@ -0,0 +1,56 @@ +password 1 +hostname rt9 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1009.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + advertise-definition + ! + flex-algo 129 + dataplane sr-mpls + advertise-definition + ! + flex-algo 130 + dataplane sr-mpls + advertise-definition + affinity include-any blue + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 9.9.9.9/32 index 9 + segment-routing prefix 9.9.9.9/32 algorithm 128 index 109 + segment-routing prefix 9.9.9.9/32 algorithm 129 index 209 + segment-routing prefix 9.9.9.9/32 algorithm 130 index 309 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf new file mode 100644 index 0000000000..3f9a8d9059 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf @@ -0,0 +1,20 @@ +log file pathd.log +! +hostname rt9 +! +segment-routing + traffic-eng + segment-list sid-algorithm-0 + index 10 mpls label 20000 + exit + segment-list sid-algorithm-128 + index 10 mpls label 20100 + exit + segment-list sid-algorithm-129 + index 10 mpls label 20200 + exit + segment-list sid-algorithm-130 + index 10 mpls label 20300 + exit + exit +exit diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json new file mode 100644 index 0000000000..e98680c5dc --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json @@ -0,0 +1,438 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20205, + "outLabelStack":[ + 20205 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf new file mode 100644 index 0000000000..378a1969be --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf @@ -0,0 +1,34 @@ +log file zebra.log +! +hostname rt9 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 9.9.9.9/32 +! +interface eth-rt3 + ip address 10.39.0.9/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt7 + ip address 10.79.0.9/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py b/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py new file mode 100755 index 0000000000..6a5f81def6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright 2021 by LINE Corporation, Hiroki Shirokura <hiroki.shirokura@linecorp.com> +# Copyright 2023 6WIND S.A. + +""" +test_isis_sr_flex_algo_topo2.py: + +[+] Flex-Algos 128 +[+] Flex-Algos 129 +[+] Flex-Algos 130 include-any blue + + +--------+ +--------+ + | | | | + | RT1 |------------------| RT2 | + | | | | + +--------+ +--------+ + / | \\ | \\ + / | \\ | \\ ++--------+ | \\ | \\ +| | | +--------+ | +--------+ +| RT0 | | | | | | | +| | | | RT4 |------------------| RT3 | ++--------+ | | | | | | + \\ | +--------+ | +--------+ + \\ | | | | \\ + +--------+ | +--------+ | \\ + | | | | | | +--------+ + | RT5 |-------|----------| RT6 | | | | + | | | | | | | RT9 | + +--------+ | +--------+ | | | + \\ | \\ | +--------+ + \\ | \\ | / + \\ | \\ | / + +--------+ +--------+ + | | | | + | RT8 |------------------| RT7 | + | | | | + +--------+ +--------+ +""" + +import os +import sys +import pytest +import json +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 + + +pytestmark = [pytest.mark.isisd] + + +def build_topo(tgen): + "Build function" + + routers = [] + for i in range(0, 10): + rt = tgen.add_router("rt{}".format(i)) + rt.run("sysctl -w net.ipv4.fib_multipath_hash_policy=1") + + def connect_routers(tgen, left_idx, right_idx): + left = "rt{}".format(left_idx) + right = "rt{}".format(right_idx) + switch = tgen.add_switch("s-{}-{}".format(left, right)) + switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right)) + switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left)) + l_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, left_idx) + tgen.gears[left].run("ip link set eth-{} down".format(right)) + tgen.gears[left].run("ip link set eth-{} address {}".format(right, l_addr)) + tgen.gears[left].run("ip link set eth-{} up".format(right)) + tgen.gears[left].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(right)) + r_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, right_idx) + tgen.gears[right].run("ip link set eth-{} down".format(left)) + tgen.gears[right].run("ip link set eth-{} address {}".format(left, r_addr)) + tgen.gears[right].run("ip link set eth-{} up".format(left)) + tgen.gears[right].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(left)) + + connect_routers(tgen, 0, 1) + connect_routers(tgen, 0, 5) + connect_routers(tgen, 1, 2) + connect_routers(tgen, 1, 4) + connect_routers(tgen, 1, 5) + connect_routers(tgen, 2, 3) + connect_routers(tgen, 2, 6) + connect_routers(tgen, 3, 4) + connect_routers(tgen, 3, 7) + connect_routers(tgen, 3, 9) + connect_routers(tgen, 4, 8) + connect_routers(tgen, 5, 6) + connect_routers(tgen, 5, 8) + connect_routers(tgen, 6, 7) + connect_routers(tgen, 7, 8) + connect_routers(tgen, 7, 9) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, 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.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))) + if rname in ["rt0", "rt9"]: + router.load_config( TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))) + router.load_config( TopoRouter.RD_PATH, os.path.join(CWD, "{}/pathd.conf".format(rname))) + router.run("ip link add dum0 type dummy") + router.run("ip link set dum0 up") + if rname == "rt0": + router.run("ip addr add 10.255.0.1/24 dev dum0") + elif rname == "rt9": + router.run("ip addr add 10.255.9.1/24 dev dum0") + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def setup_testcase(msg): + logger.info(msg) + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + return tgen + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def check_rib(name, cmd, expected_file): + def _check(name, cmd, expected_file): + logger.info("polling") + tgen = get_topogen() + router = tgen.gears[name] + output = json.loads(router.vtysh_cmd(cmd)) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + logger.info("[+] check {} \"{}\" {}".format(name, cmd, expected_file)) + tgen = get_topogen() + func = partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=120, wait=0.5) + assert result is None, "Failed" + + +def test_rib(): + check_rib("rt0", "show mpls table json", "rt0/step1/route.json") + check_rib("rt1", "show mpls table json", "rt1/step1/route.json") + check_rib("rt2", "show mpls table json", "rt2/step1/route.json") + check_rib("rt3", "show mpls table json", "rt3/step1/route.json") + check_rib("rt4", "show mpls table json", "rt4/step1/route.json") + check_rib("rt5", "show mpls table json", "rt5/step1/route.json") + check_rib("rt6", "show mpls table json", "rt6/step1/route.json") + check_rib("rt7", "show mpls table json", "rt7/step1/route.json") + check_rib("rt8", "show mpls table json", "rt8/step1/route.json") + check_rib("rt9", "show mpls table json", "rt9/step1/route.json") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis_te_topo1/reference/ted_step1.json b/tests/topotests/isis_te_topo1/reference/ted_step1.json index d7711b7e59..b70626e85d 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step1.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step1.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -91,7 +91,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -132,7 +132,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -173,7 +173,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -215,7 +215,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -263,7 +263,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -306,7 +306,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -350,7 +350,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -391,7 +391,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -432,7 +432,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -473,7 +473,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -514,7 +514,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -556,7 +556,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -600,7 +600,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step10.json b/tests/topotests/isis_te_topo1/reference/ted_step10.json index 7d017b3430..f029e5aab0 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step10.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step10.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -108,7 +108,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -148,7 +148,7 @@ } }, { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -189,7 +189,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -230,7 +230,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -271,7 +271,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -313,7 +313,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -361,7 +361,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -404,7 +404,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -462,7 +462,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -503,7 +503,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -544,7 +544,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -585,7 +585,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -626,7 +626,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -668,7 +668,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -711,7 +711,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step2.json b/tests/topotests/isis_te_topo1/reference/ted_step2.json index 7b2074b69c..aa2bafb15a 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step2.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step2.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -91,7 +91,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -133,7 +133,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -181,7 +181,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -224,7 +224,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -268,7 +268,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -309,7 +309,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -350,7 +350,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -392,7 +392,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -436,7 +436,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step3.json b/tests/topotests/isis_te_topo1/reference/ted_step3.json index 528138477a..de6d108bb3 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step3.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step3.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -94,7 +94,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -134,7 +134,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -175,7 +175,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -217,7 +217,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -265,7 +265,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -308,7 +308,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -352,7 +352,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -393,7 +393,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -434,7 +434,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -476,7 +476,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -520,7 +520,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step4.json b/tests/topotests/isis_te_topo1/reference/ted_step4.json index 528138477a..de6d108bb3 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step4.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step4.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -94,7 +94,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -134,7 +134,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -175,7 +175,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -217,7 +217,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -265,7 +265,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -308,7 +308,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -352,7 +352,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -393,7 +393,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -434,7 +434,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -476,7 +476,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -520,7 +520,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step5.json b/tests/topotests/isis_te_topo1/reference/ted_step5.json index 72e441d186..7daee99297 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step5.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step5.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -94,7 +94,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -134,7 +134,7 @@ } }, { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -175,7 +175,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -216,7 +216,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -257,7 +257,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -299,7 +299,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -347,7 +347,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -390,7 +390,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -434,7 +434,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -475,7 +475,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -516,7 +516,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -557,7 +557,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -598,7 +598,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -640,7 +640,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -684,7 +684,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step6.json b/tests/topotests/isis_te_topo1/reference/ted_step6.json index a5f50c3eba..289eb1ebc2 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step6.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step6.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -94,7 +94,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -134,7 +134,7 @@ } }, { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -175,7 +175,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -216,7 +216,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -257,7 +257,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -299,7 +299,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -347,7 +347,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -390,7 +390,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -434,7 +434,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -475,7 +475,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -516,7 +516,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -557,7 +557,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -598,7 +598,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -640,7 +640,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -683,7 +683,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step7.json b/tests/topotests/isis_te_topo1/reference/ted_step7.json index 447febce48..18eb42fd32 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step7.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step7.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -109,7 +109,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -149,7 +149,7 @@ } }, { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -190,7 +190,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -231,7 +231,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -272,7 +272,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -314,7 +314,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -362,7 +362,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -405,7 +405,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -464,7 +464,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -505,7 +505,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -546,7 +546,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -587,7 +587,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -628,7 +628,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -670,7 +670,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -713,7 +713,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step8.json b/tests/topotests/isis_te_topo1/reference/ted_step8.json index 510e034eba..ede36cf93d 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step8.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step8.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -109,7 +109,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -149,7 +149,7 @@ } }, { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -190,7 +190,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -231,7 +231,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -272,7 +272,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -314,7 +314,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -362,7 +362,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -405,7 +405,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -464,7 +464,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -505,7 +505,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -546,7 +546,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -587,7 +587,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -628,7 +628,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -670,7 +670,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -713,7 +713,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/kinds.yaml b/tests/topotests/kinds.yaml new file mode 100644 index 0000000000..127790ed07 --- /dev/null +++ b/tests/topotests/kinds.yaml @@ -0,0 +1,30 @@ +version: 1 +kinds: + - name: frr + cmd: | + chown frr:frr -R /var/run/frr + chown frr:frr -R /var/log/frr + /usr/lib/frr/frrinit.sh start + tail -F /var/log/frr/frr.log + cleanup-cmd: | + /usr/lib/frr/frrinit.sh stop + volumes: + - "./%NAME%:/etc/frr" + - "%RUNDIR%/var.log.frr:/var/log/frr" + - "%RUNDIR%/var.run.frr:/var/run/frr" + cap-add: + - SYS_ADMIN + - AUDIT_WRITE + merge: ["volumes"] +cli: + commands: + - name: "" + exec: "vtysh -c '{}'" + format: "[ROUTER ...] COMMAND" + help: "execute vtysh COMMAND on the router[s]" + kinds: ["frr"] + - name: "vtysh" + exec: "/usr/bin/vtysh" + format: "vtysh ROUTER [ROUTER ...]" + new-window: true + kinds: ["frr"] diff --git a/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json index 63281e9be3..c1c231de3d 100644 --- a/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json @@ -2,9 +2,9 @@ "neighbors":{ "2.2.2.2":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.1.2", + "ifaceAddress":"10.0.1.2", "ifaceName":"r1-eth0:10.0.1.1" } ] diff --git a/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json index f361d605ce..ee69af5e23 100644 --- a/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json @@ -2,25 +2,25 @@ "neighbors":{ "1.1.1.1":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth0:10.0.1.2" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r2-eth1:10.0.2.2" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r2-eth1:10.0.2.2" } ] diff --git a/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json index 38794357ff..3f76542e94 100644 --- a/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json @@ -2,17 +2,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r3-eth0:10.0.2.3" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r3-eth0:10.0.2.3" } ] diff --git a/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json index fccca693b9..5395cd25c9 100644 --- a/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json @@ -3,17 +3,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r4-eth0:10.0.2.4" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r4-eth0:10.0.2.4" } ] diff --git a/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json index 63281e9be3..c1c231de3d 100644 --- a/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json @@ -2,9 +2,9 @@ "neighbors":{ "2.2.2.2":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.1.2", + "ifaceAddress":"10.0.1.2", "ifaceName":"r1-eth0:10.0.1.1" } ] diff --git a/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json index f361d605ce..ee69af5e23 100644 --- a/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json @@ -2,25 +2,25 @@ "neighbors":{ "1.1.1.1":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth0:10.0.1.2" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r2-eth1:10.0.2.2" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r2-eth1:10.0.2.2" } ] diff --git a/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json index 38794357ff..3f76542e94 100644 --- a/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json @@ -2,17 +2,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r3-eth0:10.0.2.3" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r3-eth0:10.0.2.3" } ] diff --git a/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json index fccca693b9..5395cd25c9 100644 --- a/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json @@ -3,17 +3,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r4-eth0:10.0.2.4" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r4-eth0:10.0.2.4" } ] diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json index 7efde22f3f..e25523d18d 100644 --- a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.1.2", + "ifaceAddress": "10.0.1.2", "ifaceName": "r1-eth1:10.0.1.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.3", + "ifaceAddress": "10.0.2.3", "ifaceName": "r1-eth2:10.0.2.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json index 5bea193e01..fa2ea86d67 100644 --- a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth1:10.0.1.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "3.3.3.3": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.3.3", + "ifaceAddress":"10.0.3.3", "ifaceName":"r2-eth2:10.0.3.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json index 9966297d8a..bf77e088d5 100644 --- a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.1", + "ifaceAddress":"10.0.2.1", "ifaceName":"r3-eth1:10.0.2.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "2.2.2.2": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.3.2", + "ifaceAddress":"10.0.3.2", "ifaceName":"r3-eth2:10.0.3.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json index d9192f1104..f47c2dfad7 100644 --- a/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json @@ -2,12 +2,12 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.1.2", - "requestCounter": 0 + "ifaceAddress": "10.0.1.2", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json index ea78592bd5..901282f876 100644 --- a/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json @@ -2,40 +2,40 @@ "neighbors": { "1.1.1.1": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.1.1", - "requestCounter": 0 + "ifaceAddress": "10.0.1.1", + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.3", - "requestCounter": 0 + "ifaceAddress": "10.0.2.3", + "linkStateRequestListCounter": 0 }, { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.3.3", - "requestCounter": 0 + "ifaceAddress": "10.0.3.3", + "linkStateRequestListCounter": 0 } ], "4.4.4.4": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.4", - "requestCounter": 0 + "ifaceAddress": "10.0.2.4", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json index d3c50247ea..164040ae3e 100644 --- a/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json @@ -2,30 +2,30 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.2", - "requestCounter": 0 + "ifaceAddress": "10.0.2.2", + "linkStateRequestListCounter": 0 }, { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.3.2", - "requestCounter": 0 + "ifaceAddress": "10.0.3.2", + "linkStateRequestListCounter": 0 } ], "4.4.4.4": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.4", - "requestCounter": 0 + "ifaceAddress": "10.0.2.4", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json index 20751a2884..98c759a6ff 100644 --- a/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json @@ -2,22 +2,22 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.2", - "requestCounter": 0 + "ifaceAddress": "10.0.2.2", + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.3", - "requestCounter": 0 + "ifaceAddress": "10.0.2.3", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json index 90c8195416..9acb4f7b8c 100644 --- a/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 2, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 2, "converged": "Full", - "address": "10.0.1.2", + "ifaceAddress": "10.0.1.2", "ifaceName": "r1-eth1:10.0.1.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 2, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 2, "converged": "Full", - "address": "10.0.2.3", + "ifaceAddress": "10.0.2.3", "ifaceName": "r1-eth2:10.0.2.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json index 29dde53c6d..6634199902 100644 --- a/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth1:10.0.1.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "3.3.3.3": [ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.3.3", + "ifaceAddress":"10.0.3.3", "ifaceName":"r2-eth2:10.0.3.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json index 9966297d8a..bf77e088d5 100644 --- a/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.1", + "ifaceAddress":"10.0.2.1", "ifaceName":"r3-eth1:10.0.2.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "2.2.2.2": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.3.2", + "ifaceAddress":"10.0.3.2", "ifaceName":"r3-eth2:10.0.3.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index e5a1e75837..d19d8db75c 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -492,7 +492,7 @@ def save_initial_config_on_routers(tgen): # Get all running configs in parallel procs = {} for rname in router_list: - logger.info("Fetching running config for router %s", rname) + logger.debug("Fetching running config for router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"], stdin=None, @@ -548,7 +548,7 @@ def reset_config_on_routers(tgen, routerName=None): # procs = {} for rname in router_list: - logger.info("Fetching running config for router %s", rname) + logger.debug("Fetching running config for router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"], stdin=None, @@ -570,7 +570,7 @@ def reset_config_on_routers(tgen, routerName=None): # procs = {} for rname in router_list: - logger.info( + logger.debug( "Generating delta for router %s to new configuration (gen %d)", rname, gen ) procs[rname] = tgen.net.popen( @@ -599,7 +599,7 @@ def reset_config_on_routers(tgen, routerName=None): # procs = {} for rname in router_list: - logger.info("Applying delta config on router %s", rname) + logger.debug("Applying delta config on router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-f", delta_fmt.format(rname, gen)], @@ -611,7 +611,7 @@ def reset_config_on_routers(tgen, routerName=None): output, _ = p.communicate() vtysh_command = "vtysh -f {}".format(delta_fmt.format(rname, gen)) if not p.returncode: - router_list[rname].logger.info( + router_list[rname].logger.debug( '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format( vtysh_command, output ) @@ -640,7 +640,7 @@ def reset_config_on_routers(tgen, routerName=None): if show_router_config: procs = {} for rname in router_list: - logger.info("Fetching running config for router %s", rname) + logger.debug("Fetching running config for router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"], stdin=None, @@ -657,7 +657,7 @@ def reset_config_on_routers(tgen, routerName=None): output, ) else: - logger.info( + logger.debug( "Configuration on router %s after reset:\n%s", rname, output ) @@ -742,7 +742,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False): frr_cfg_bkup = frr_cfg_bkup_fmt.format(rname) with open(frr_cfg_file, "r+") as cfg: data = cfg.read() - logger.info( + logger.debug( "Applying following configuration on router %s (gen: %d):\n%s", rname, gen, @@ -775,7 +775,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False): frr_cfg_file = frr_cfg_file_fmt.format(rname) vtysh_command = "vtysh -f " + frr_cfg_file if not p.returncode: - router_list[rname].logger.info( + router_list[rname].logger.debug( '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format( vtysh_command, output ) @@ -821,7 +821,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False): output, ) else: - logger.info("New configuration for router %s:\n%s", rname, output) + logger.debug("New configuration for router %s:\n%s", rname, output) logger.debug("Exiting API: load_config_to_routers") return not errors @@ -957,10 +957,10 @@ def generate_support_bundle(): bundle_procs[rname] = tgen.net[rname].popen(gen_sup_cmd, stdin=None) for rname, rnode in router_list.items(): - logger.info("Waiting on support bundle for %s", rname) + logger.debug("Waiting on support bundle for %s", rname) output, error = bundle_procs[rname].communicate() if output: - logger.info( + logger.debug( "Output from collecting support bundle for %s:\n%s", rname, output ) if error: @@ -1234,15 +1234,15 @@ def add_interfaces_to_vlan(tgen, input_dict): cmd = "ip link add link {} name {} type vlan id {}".format( interface, vlan_intf, vlan ) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) result = rnode.run(cmd) - logger.info("result %s", result) + logger.debug("result %s", result) # Bringing interface up cmd = "ip link set {} up".format(vlan_intf) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) result = rnode.run(cmd) - logger.info("result %s", result) + logger.debug("result %s", result) # Assigning IP address ifaddr = ipaddress.ip_interface( @@ -1254,9 +1254,9 @@ def add_interfaces_to_vlan(tgen, input_dict): cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format( ifaddr.version, vlan_intf, ifaddr ) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) result = rnode.run(cmd) - logger.info("result %s", result) + logger.debug("result %s", result) def tcpdump_capture_start( @@ -1567,12 +1567,16 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): vrf["name"], vrf["id"] ) - logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd) + logger.debug( + "[DUT: %s]: Running kernel cmd [%s]", c_router, cmd + ) rnode.run(cmd) # Kernel cmd - Bring down VRF cmd = "ip link set dev {} down".format(name) - logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd) + logger.debug( + "[DUT: %s]: Running kernel cmd [%s]", c_router, cmd + ) rnode.run(cmd) else: @@ -1581,14 +1585,14 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): cmd = "ip link add {} type vrf table {}".format( name, table_id ) - logger.info( + logger.debug( "[DUT: %s]: Running kernel cmd " "[%s]", c_router, cmd ) rnode.run(cmd) # Kernel cmd - Bring up VRF cmd = "ip link set dev {} up".format(name) - logger.info( + logger.debug( "[DUT: %s]: Running kernel " "cmd [%s]", c_router, cmd ) rnode.run(cmd) @@ -1616,7 +1620,7 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): interface_name, _vrf ) - logger.info( + logger.debug( "[DUT: %s]: Running" " kernel cmd [%s]", c_router, cmd, @@ -1683,7 +1687,7 @@ def create_interface_in_kernel( cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format( ifaddr.version, name, ifaddr ) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) rnode.run(cmd) if vrf: @@ -1715,7 +1719,7 @@ def shutdown_bringup_interface_in_kernel(tgen, dut, intf_name, ifaceaction=False action = "down" cmd = "{} {} {}".format(cmd, intf_name, action) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) rnode.run(cmd) @@ -1968,7 +1972,7 @@ def retry(retry_timeout, initial_wait=0, expected=True, diag_pct=0.75): ) if initial_wait > 0: - logger.info("Waiting for [%s]s as initial delay", initial_wait) + logger.debug("Waiting for [%s]s as initial delay", initial_wait) sleep(initial_wait) invert_logic = not _expected @@ -2027,13 +2031,13 @@ def retry(retry_timeout, initial_wait=0, expected=True, diag_pct=0.75): return saved_failure if saved_failure: - logger.info( + logger.debug( "RETRY DIAG: [failure] Sleeping %ds until next retry with %.1f retry time left - too see if timeout was too short", retry_sleep, seconds_left, ) else: - logger.info( + logger.debug( "Sleeping %ds until next retry with %.1f retry time left", retry_sleep, seconds_left, @@ -3357,7 +3361,19 @@ def socat_send_mld_join( # Run socat command to send IGMP join logger.info("[DUT: {}]: Running command: [{}]".format(server, socat_cmd)) - output = rnode.run("set +m; {} sleep 0.5".format(socat_cmd)) + output = rnode.run("set +m; {} echo $!".format(socat_cmd)) + + # Check if socat join process is running + if output: + pid = output.split()[0] + rnode.run("touch /var/run/frr/socat_join.pid") + rnode.run("echo %s >> /var/run/frr/socat_join.pid" % pid) + else: + errormsg = "Socat join is not sent for {}. Error {}".format( + mld_group, output + ) + logger.error(output) + return errormsg logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True @@ -3415,7 +3431,7 @@ def socat_send_pim6_traffic( if multicast_hops: socat_cmd += "multicast-hops=255'" - socat_cmd += " &>{}/socat.logs &".format(tgen.logdir) + socat_cmd += " >{}/socat.logs &".format(tgen.logdir) # Run socat command to send pim6 traffic logger.info( @@ -3435,7 +3451,20 @@ def socat_send_pim6_traffic( ) rnode.run("chmod 755 {}".format(traffic_shell_script)) - output = rnode.run("{} &> /dev/null".format(traffic_shell_script)) + output = rnode.run("{} &>/dev/null & echo $!".format(traffic_shell_script)) + + # Check if socat traffic process is running + if output: + pid = output.split()[0] + rnode.run("touch /var/run/frr/socat_traffic.pid") + rnode.run("echo %s >> /var/run/frr/socat_traffic.pid" % pid) + + else: + errormsg = "Socat traffic is not sent for {}. Error {}".format( + mld_group, output + ) + logger.error(output) + return errormsg logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True @@ -3465,18 +3494,30 @@ def kill_socat(tgen, dut=None, action=None): if dut is not None and router != dut: continue + traffic_shell_script = "{}/{}/traffic.sh".format(tgen.logdir, router) + pid_socat_join = rnode.run("cat /var/run/frr/socat_join.pid") + pid_socat_traffic = rnode.run("cat /var/run/frr/socat_traffic.pid") if action == "remove_mld_join": - cmd = "ps -ef | grep socat | grep UDP6-RECV | grep {}".format(router) + pids = pid_socat_join elif action == "remove_mld_traffic": - cmd = "ps -ef | grep socat | grep UDP6-SEND | grep {}".format(router) + pids = pid_socat_traffic else: - cmd = "ps -ef | grep socat".format(router) - - awk_cmd = "awk -F' ' '{print $2}' | xargs kill -9 &>/dev/null &" - cmd = "{} | {}".format(cmd, awk_cmd) + pids = "\n".join([pid_socat_join, pid_socat_traffic]) - logger.debug("[DUT: {}]: Running command: [{}]".format(router, cmd)) - rnode.run(cmd) + if os.path.exists(traffic_shell_script): + cmd = ( + "ps -ef | grep %s | awk -F' ' '{print $2}' | xargs kill -9" + % traffic_shell_script + ) + logger.debug("[DUT: {}]: Running command: [{}]".format(router, cmd)) + rnode.run(cmd) + + 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)) diff --git a/tests/topotests/lib/grpc-query.py b/tests/topotests/lib/grpc-query.py index 6457bbefdd..5dd12d581e 100755 --- a/tests/topotests/lib/grpc-query.py +++ b/tests/topotests/lib/grpc-query.py @@ -21,7 +21,8 @@ try: import grpc import grpc_tools - from micronet import commander + sys.path.append(os.path.dirname(CWD)) + from munet.base import commander commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .") commander.cmd_raises( diff --git a/tests/topotests/lib/micronet.py b/tests/topotests/lib/micronet.py index 1381009168..f4aa8278f1 100644 --- a/tests/topotests/lib/micronet.py +++ b/tests/topotests/lib/micronet.py @@ -3,1004 +3,22 @@ # # July 9 2021, Christian Hopps <chopps@labn.net> # -# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2021-2023, LabN Consulting, L.L.C. # -import datetime -import logging -import os -import re -import shlex -import subprocess -import sys -import tempfile -import time as time_mod -import traceback - -root_hostname = subprocess.check_output("hostname") - -# This allows us to cleanup any leftovers later on -os.environ["MICRONET_PID"] = str(os.getpid()) - - -class Timeout(object): - def __init__(self, delta): - self.started_on = datetime.datetime.now() - self.expires_on = self.started_on + datetime.timedelta(seconds=delta) - - def elapsed(self): - elapsed = datetime.datetime.now() - self.started_on - return elapsed.total_seconds() - - def is_expired(self): - return datetime.datetime.now() > self.expires_on - - -def is_string(value): - """Return True if value is a string.""" - try: - return isinstance(value, basestring) # type: ignore - except NameError: - return isinstance(value, str) - - -def shell_quote(command): - """Return command wrapped in single quotes.""" - if sys.version_info[0] >= 3: - return shlex.quote(command) - return "'{}'".format(command.replace("'", "'\"'\"'")) # type: ignore - - -def cmd_error(rc, o, e): - s = "rc {}".format(rc) - o = "\n\tstdout: " + o.strip() if o and o.strip() else "" - e = "\n\tstderr: " + e.strip() if e and e.strip() else "" - return s + o + e - - -def proc_error(p, o, e): - args = p.args if is_string(p.args) else " ".join(p.args) - s = "rc {} pid {}\n\targs: {}".format(p.returncode, p.pid, args) - o = "\n\tstdout: " + o.strip() if o and o.strip() else "" - e = "\n\tstderr: " + e.strip() if e and e.strip() else "" - return s + o + e - - -def comm_error(p): - rc = p.poll() - assert rc is not None - if not hasattr(p, "saved_output"): - p.saved_output = p.communicate() - return proc_error(p, *p.saved_output) - - -class Commander(object): # pylint: disable=R0205 - """ - Commander. - - An object that can execute commands. - """ - - tmux_wait_gen = 0 - - def __init__(self, name, logger=None): - """Create a Commander.""" - self.name = name - self.last = None - self.exec_paths = {} - self.pre_cmd = [] - self.pre_cmd_str = "" - - if not logger: - self.logger = logging.getLogger(__name__ + ".commander." + name) - else: - self.logger = logger - - self.cwd = self.cmd_raises("pwd").strip() - - def set_logger(self, logfile): - self.logger = logging.getLogger(__name__ + ".commander." + self.name) - if is_string(logfile): - handler = logging.FileHandler(logfile, mode="w") - else: - handler = logging.StreamHandler(logfile) - - fmtstr = "%(asctime)s.%(msecs)03d %(levelname)s: {}({}): %(message)s".format( - self.__class__.__name__, self.name - ) - handler.setFormatter(logging.Formatter(fmt=fmtstr)) - self.logger.addHandler(handler) - - def set_pre_cmd(self, pre_cmd=None): - if not pre_cmd: - self.pre_cmd = [] - self.pre_cmd_str = "" - else: - self.pre_cmd = pre_cmd - self.pre_cmd_str = " ".join(self.pre_cmd) + " " - - def __str__(self): - return "Commander({})".format(self.name) - - def get_exec_path(self, binary): - """Return the full path to the binary executable. - - `binary` :: binary name or list of binary names - """ - if is_string(binary): - bins = [binary] - else: - bins = binary - for b in bins: - if b in self.exec_paths: - return self.exec_paths[b] - - rc, output, _ = self.cmd_status("which " + b, warn=False) - if not rc: - return os.path.abspath(output.strip()) - return None - - def get_tmp_dir(self, uniq): - return os.path.join(tempfile.mkdtemp(), uniq) - - def test(self, flags, arg): - """Run test binary, with flags and arg""" - test_path = self.get_exec_path(["test"]) - rc, output, _ = self.cmd_status([test_path, flags, arg], warn=False) - return not rc - - def path_exists(self, path): - """Check if path exists.""" - return self.test("-e", path) - - def _get_cmd_str(self, cmd): - if is_string(cmd): - return self.pre_cmd_str + cmd - cmd = self.pre_cmd + cmd - return " ".join(cmd) - - def _get_sub_args(self, cmd, defaults, **kwargs): - if is_string(cmd): - defaults["shell"] = True - pre_cmd = self.pre_cmd_str - else: - defaults["shell"] = False - pre_cmd = self.pre_cmd - cmd = [str(x) for x in cmd] - defaults.update(kwargs) - return pre_cmd, cmd, defaults - - def _popen(self, method, cmd, skip_pre_cmd=False, **kwargs): - if sys.version_info[0] >= 3: - defaults = { - "encoding": "utf-8", - "stdout": subprocess.PIPE, - "stderr": subprocess.PIPE, - } - else: - defaults = { - "stdout": subprocess.PIPE, - "stderr": subprocess.PIPE, - } - pre_cmd, cmd, defaults = self._get_sub_args(cmd, defaults, **kwargs) - - self.logger.debug('%s: %s("%s", kwargs: %s)', self, method, cmd, defaults) - - actual_cmd = cmd if skip_pre_cmd else pre_cmd + cmd - p = subprocess.Popen(actual_cmd, **defaults) - if not hasattr(p, "args"): - p.args = actual_cmd - return p, actual_cmd - - def set_cwd(self, cwd): - self.logger.warning("%s: 'cd' (%s) does not work outside namespaces", self, cwd) - self.cwd = cwd - - def popen(self, cmd, **kwargs): - """ - Creates a pipe with the given `command`. - - Args: - command: `str` or `list` of command to open a pipe with. - **kwargs: kwargs is eventually passed on to Popen. If `command` is a string - then will be invoked with shell=True, otherwise `command` is a list and - will be invoked with shell=False. - - Returns: - a subprocess.Popen object. - """ - p, _ = self._popen("popen", cmd, **kwargs) - return p - - def cmd_status(self, cmd, raises=False, warn=True, stdin=None, **kwargs): - """Execute a command.""" - - # We are not a shell like mininet, so we need to intercept this - chdir = False - if not is_string(cmd): - cmds = cmd - else: - # XXX we can drop this when the code stops assuming it works - m = re.match(r"cd(\s*|\s+(\S+))$", cmd) - if m and m.group(2): - self.logger.warning( - "Bad call to 'cd' (chdir) emulating, use self.set_cwd():\n%s", - "".join(traceback.format_stack(limit=12)), - ) - assert is_string(cmd) - chdir = True - cmd += " && pwd" - - # If we are going to run under bash then we don't need shell=True! - cmds = ["/bin/bash", "-c", cmd] - - pinput = None - - if is_string(stdin) or isinstance(stdin, bytes): - pinput = stdin - stdin = subprocess.PIPE - - p, actual_cmd = self._popen("cmd_status", cmds, stdin=stdin, **kwargs) - stdout, stderr = p.communicate(input=pinput) - rc = p.wait() - - # For debugging purposes. - self.last = (rc, actual_cmd, cmd, stdout, stderr) - - if rc: - if warn: - self.logger.warning( - "%s: proc failed: %s:", self, proc_error(p, stdout, stderr) - ) - if raises: - # error = Exception("stderr: {}".format(stderr)) - # This annoyingly doesn't' show stderr when printed normally - error = subprocess.CalledProcessError(rc, actual_cmd) - error.stdout, error.stderr = stdout, stderr - raise error - elif chdir: - self.set_cwd(stdout.strip()) - - return rc, stdout, stderr - - def cmd_legacy(self, cmd, **kwargs): - """Execute a command with stdout and stderr joined, *IGNORES ERROR*.""" - - defaults = {"stderr": subprocess.STDOUT} - defaults.update(kwargs) - _, stdout, _ = self.cmd_status(cmd, raises=False, **defaults) - return stdout - - def cmd_raises(self, cmd, **kwargs): - """Execute a command. Raise an exception on errors""" - - rc, stdout, _ = self.cmd_status(cmd, raises=True, **kwargs) - assert rc == 0 - return stdout - - # Run a command in a new window (gnome-terminal, screen, tmux, xterm) - def run_in_window( - self, - cmd, - wait_for=False, - background=False, - name=None, - title=None, - forcex=False, - new_window=False, - tmux_target=None, - ): - """ - Run a command in a new window (TMUX, Screen or XTerm). - - Args: - wait_for: True to wait for exit from command or `str` as channel neme to signal on exit, otherwise False - background: Do not change focus to new window. - title: Title for new pane (tmux) or window (xterm). - name: Name of the new window (tmux) - forcex: Force use of X11. - new_window: Open new window (instead of pane) in TMUX - tmux_target: Target for tmux pane. - - Returns: - the pane/window identifier from TMUX (depends on `new_window`) - """ - - channel = None - if is_string(wait_for): - channel = wait_for - elif wait_for is True: - channel = "{}-wait-{}".format(os.getpid(), Commander.tmux_wait_gen) - Commander.tmux_wait_gen += 1 - - sudo_path = self.get_exec_path(["sudo"]) - nscmd = sudo_path + " " + self.pre_cmd_str + cmd - if "TMUX" in os.environ and not forcex: - cmd = [self.get_exec_path("tmux")] - if new_window: - cmd.append("new-window") - cmd.append("-P") - if name: - cmd.append("-n") - cmd.append(name) - if tmux_target: - cmd.append("-t") - cmd.append(tmux_target) - else: - cmd.append("split-window") - cmd.append("-P") - cmd.append("-h") - if not tmux_target: - tmux_target = os.getenv("TMUX_PANE", "") - if background: - cmd.append("-d") - if tmux_target: - cmd.append("-t") - cmd.append(tmux_target) - if title: - nscmd = "printf '\033]2;{}\033\\'; {}".format(title, nscmd) - if channel: - nscmd = 'trap "tmux wait -S {}; exit 0" EXIT; {}'.format(channel, nscmd) - cmd.append(nscmd) - elif "STY" in os.environ and not forcex: - # wait for not supported in screen for now - channel = None - cmd = [self.get_exec_path("screen")] - if title: - cmd.append("-t") - cmd.append(title) - if not os.path.exists( - "/run/screen/S-{}/{}".format(os.environ["USER"], os.environ["STY"]) - ): - cmd = ["sudo", "-u", os.environ["SUDO_USER"]] + cmd - cmd.extend(nscmd.split(" ")) - elif "DISPLAY" in os.environ: - # We need it broken up for xterm - user_cmd = cmd - cmd = [self.get_exec_path("xterm")] - if "SUDO_USER" in os.environ: - cmd = [self.get_exec_path("sudo"), "-u", os.environ["SUDO_USER"]] + cmd - if title: - cmd.append("-T") - cmd.append(title) - cmd.append("-e") - cmd.append(sudo_path) - cmd.extend(self.pre_cmd) - cmd.extend(["bash", "-c", user_cmd]) - # if channel: - # return self.cmd_raises(cmd, skip_pre_cmd=True) - # else: - p = self.popen( - cmd, - skip_pre_cmd=True, - stdin=None, - shell=False, - ) - time_mod.sleep(2) - if p.poll() is not None: - self.logger.error("%s: Failed to launch xterm: %s", self, comm_error(p)) - return p - else: - self.logger.error( - "DISPLAY, STY, and TMUX not in environment, can't open window" - ) - raise Exception("Window requestd but TMUX, Screen and X11 not available") - - pane_info = self.cmd_raises(cmd, skip_pre_cmd=True).strip() - - # Re-adjust the layout - if "TMUX" in os.environ: - self.cmd_status( - "tmux select-layout -t {} tiled".format( - pane_info if not tmux_target else tmux_target - ), - skip_pre_cmd=True, - ) - - # Wait here if we weren't handed the channel to wait for - if channel and wait_for is True: - cmd = [self.get_exec_path("tmux"), "wait", channel] - self.cmd_status(cmd, skip_pre_cmd=True) - - return pane_info - - def delete(self): - pass - - -class LinuxNamespace(Commander): - """ - A linux Namespace. - - An object that creates and executes commands in a linux namespace - """ - - def __init__( - self, - name, - net=True, - mount=True, - uts=True, - cgroup=False, - ipc=False, - pid=False, - time=False, - user=False, - set_hostname=True, - private_mounts=None, - logger=None, - ): - """ - Create a new linux namespace. - - Args: - name: Internal name for the namespace. - net: Create network namespace. - mount: Create network namespace. - uts: Create UTS (hostname) namespace. - cgroup: Create cgroup namespace. - ipc: Create IPC namespace. - pid: Create PID namespace, also mounts new /proc. - time: Create time namespace. - user: Create user namespace, also keeps capabilities. - set_hostname: Set the hostname to `name`, uts must also be True. - private_mounts: List of strings of the form - "[/external/path:]/internal/path. If no external path is specified a - tmpfs is mounted on the internal path. Any paths specified are first - passed to `mkdir -p`. - logger: Passed to superclass. - """ - super(LinuxNamespace, self).__init__(name, logger) - - self.logger.debug("%s: Creating", self) - - self.intfs = [] - - nslist = [] - cmd = ["/usr/bin/unshare"] - flags = "" - self.a_flags = [] - self.ifnetns = {} - - if cgroup: - nslist.append("cgroup") - flags += "C" - if ipc: - nslist.append("ipc") - flags += "i" - if mount: - nslist.append("mnt") - flags += "m" - if net: - nslist.append("net") - flags += "n" - if pid: - nslist.append("pid") - flags += "f" - flags += "p" - cmd.append("--mount-proc") - if time: - # XXX this filename is probably wrong - nslist.append("time") - flags += "T" - if user: - nslist.append("user") - flags += "U" - cmd.append("--keep-caps") - if uts: - nslist.append("uts") - flags += "u" - - if flags: - aflags = flags.replace("f", "") - if aflags: - self.a_flags = ["-" + x for x in aflags] - cmd.extend(["-" + x for x in flags]) - - if pid: - cmd.append(commander.get_exec_path("tini")) - cmd.append("-vvv") - cmd.append("/bin/cat") - - # Using cat and a stdin PIPE is nice as it will exit when we do. However, we - # also detach it from the pgid so that signals do not propagate to it. This is - # b/c it would exit early (e.g., ^C) then, at least the main micronet proc which - # has no other processes like frr daemons running, will take the main network - # namespace with it, which will remove the bridges and the veth pair (because - # the bridge side veth is deleted). - self.logger.debug("%s: creating namespace process: %s", self, cmd) - p = subprocess.Popen( - cmd, - stdin=subprocess.PIPE, - stdout=open("/dev/null", "w"), - stderr=open("/dev/null", "w"), - text=True, - start_new_session=True, # detach from pgid so signals don't propagate - shell=False, - ) - self.p = p - self.pid = p.pid - - self.logger.debug("%s: namespace pid: %d", self, self.pid) - - # ----------------------------------------------- - # Now let's wait until unshare completes it's job - # ----------------------------------------------- - timeout = Timeout(30) - while p.poll() is None and not timeout.is_expired(): - for fname in tuple(nslist): - ours = os.readlink("/proc/self/ns/{}".format(fname)) - theirs = os.readlink("/proc/{}/ns/{}".format(self.pid, fname)) - # See if their namespace is different - if ours != theirs: - nslist.remove(fname) - if not nslist: - break - elapsed = int(timeout.elapsed()) - if elapsed <= 3: - time_mod.sleep(0.1) - elif elapsed > 10: - self.logger.warning("%s: unshare taking more than %ss", self, elapsed) - time_mod.sleep(3) - else: - self.logger.info("%s: unshare taking more than %ss", self, elapsed) - time_mod.sleep(1) - assert p.poll() is None, "unshare unexpectedly exited!" - assert not nslist, "unshare never unshared!" - - # Set pre-command based on our namespace proc - self.base_pre_cmd = ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid)] - if not pid: - self.base_pre_cmd.append("-F") - self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + self.cwd]) - - # Remount sysfs and cgroup to pickup any changes - self.cmd_raises("mount -t sysfs sysfs /sys") - self.cmd_raises( - "mount -o rw,nosuid,nodev,noexec,relatime -t cgroup2 cgroup /sys/fs/cgroup" - ) - - # Set the hostname to the namespace name - if uts and set_hostname: - # Debugging get the root hostname - self.cmd_raises("hostname " + self.name) - nroot = subprocess.check_output("hostname") - if root_hostname != nroot: - result = self.p.poll() - assert root_hostname == nroot, "STATE of namespace process {}".format( - result - ) - - if private_mounts: - if is_string(private_mounts): - private_mounts = [private_mounts] - for m in private_mounts: - s = m.split(":", 1) - if len(s) == 1: - self.tmpfs_mount(s[0]) - else: - self.bind_mount(s[0], s[1]) - - o = self.cmd_legacy("ls -l /proc/{}/ns".format(self.pid)) - self.logger.debug("namespaces:\n %s", o) - - # Doing this here messes up all_protocols ipv6 check - self.cmd_raises("ip link set lo up") - - def __str__(self): - return "LinuxNamespace({})".format(self.name) - - def tmpfs_mount(self, inner): - self.cmd_raises("mkdir -p " + inner) - self.cmd_raises("mount -n -t tmpfs tmpfs " + inner) - - def bind_mount(self, outer, inner): - self.cmd_raises("mkdir -p " + inner) - self.cmd_raises("mount --rbind {} {} ".format(outer, inner)) - - def add_vlan(self, vlanname, linkiface, vlanid): - self.logger.debug("Adding VLAN interface: %s (%s)", vlanname, vlanid) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises( - [ - ip_path, - "link", - "add", - "link", - linkiface, - "name", - vlanname, - "type", - "vlan", - "id", - vlanid, - ] - ) - self.cmd_raises([ip_path, "link", "set", "dev", vlanname, "up"]) - - def add_loop(self, loopname): - self.logger.debug("Adding Linux iface: %s", loopname) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises([ip_path, "link", "add", loopname, "type", "dummy"]) - self.cmd_raises([ip_path, "link", "set", "dev", loopname, "up"]) - - def add_l3vrf(self, vrfname, tableid): - self.logger.debug("Adding Linux VRF: %s", vrfname) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises( - [ip_path, "link", "add", vrfname, "type", "vrf", "table", tableid] - ) - self.cmd_raises([ip_path, "link", "set", "dev", vrfname, "up"]) - - def del_iface(self, iface): - self.logger.debug("Removing Linux Iface: %s", iface) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises([ip_path, "link", "del", iface]) - - def attach_iface_to_l3vrf(self, ifacename, vrfname): - self.logger.debug("Attaching Iface %s to Linux VRF %s", ifacename, vrfname) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - if vrfname: - self.cmd_raises( - [ip_path, "link", "set", "dev", ifacename, "master", vrfname] - ) - else: - self.cmd_raises([ip_path, "link", "set", "dev", ifacename, "nomaster"]) - - def add_netns(self, ns): - self.logger.debug("Adding network namespace %s", ns) - - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - if os.path.exists("/run/netns/{}".format(ns)): - self.logger.warning("%s: Removing existing nsspace %s", self, ns) - try: - self.delete_netns(ns) - except Exception as ex: - self.logger.warning( - "%s: Couldn't remove existing nsspace %s: %s", - self, - ns, - str(ex), - exc_info=True, - ) - self.cmd_raises([ip_path, "netns", "add", ns]) - - def delete_netns(self, ns): - self.logger.debug("Deleting network namespace %s", ns) - - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises([ip_path, "netns", "delete", ns]) - - def set_intf_netns(self, intf, ns, up=False): - # In case a user hard-codes 1 thinking it "resets" - ns = str(ns) - if ns == "1": - ns = str(self.pid) - - self.logger.debug("Moving interface %s to namespace %s", intf, ns) - - cmd = "ip link set {} netns " + ns - if up: - cmd += " up" - self.intf_ip_cmd(intf, cmd) - if ns == str(self.pid): - # If we are returning then remove from dict - if intf in self.ifnetns: - del self.ifnetns[intf] - else: - self.ifnetns[intf] = ns - - def reset_intf_netns(self, intf): - self.logger.debug("Moving interface %s to default namespace", intf) - self.set_intf_netns(intf, str(self.pid)) - - def intf_ip_cmd(self, intf, cmd): - """Run an ip command for considering an interfaces possible namespace. - - `cmd` - format is run using the interface name on the command - """ - if intf in self.ifnetns: - assert cmd.startswith("ip ") - cmd = "ip -n " + self.ifnetns[intf] + cmd[2:] - self.cmd_raises(cmd.format(intf)) - - def set_cwd(self, cwd): - # Set pre-command based on our namespace proc - self.logger.debug("%s: new CWD %s", self, cwd) - self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + cwd]) - - def register_interface(self, ifname): - if ifname not in self.intfs: - self.intfs.append(ifname) - - def delete(self): - if self.p and self.p.poll() is None: - if sys.version_info[0] >= 3: - try: - self.p.terminate() - self.p.communicate(timeout=10) - except subprocess.TimeoutExpired: - self.p.kill() - self.p.communicate(timeout=2) - else: - self.p.kill() - self.p.communicate() - self.set_pre_cmd(["/bin/false"]) - - -class SharedNamespace(Commander): - """ - Share another namespace. - - An object that executes commands in an existing pid's linux namespace - """ - - def __init__(self, name, pid, aflags=("-a",), logger=None): - """ - Share a linux namespace. - - Args: - name: Internal name for the namespace. - pid: PID of the process to share with. - """ - super(SharedNamespace, self).__init__(name, logger) - - self.logger.debug("%s: Creating", self) - - self.pid = pid - self.intfs = [] - self.a_flags = aflags - - # Set pre-command based on our namespace proc - self.set_pre_cmd( - ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid), "--wd=" + self.cwd] - ) - - def __str__(self): - return "SharedNamespace({})".format(self.name) - - def set_cwd(self, cwd): - # Set pre-command based on our namespace proc - self.logger.debug("%s: new CWD %s", self, cwd) - self.set_pre_cmd( - ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid), "--wd=" + cwd] - ) - - def register_interface(self, ifname): - if ifname not in self.intfs: - self.intfs.append(ifname) - - -class Bridge(SharedNamespace): - """ - A linux bridge. - """ - - next_brid_ord = 0 - - @classmethod - def _get_next_brid(cls): - brid_ord = cls.next_brid_ord - cls.next_brid_ord += 1 - return brid_ord - - def __init__(self, name=None, unet=None, logger=None): - """Create a linux Bridge.""" - - self.unet = unet - self.brid_ord = self._get_next_brid() - if name: - self.brid = name - else: - self.brid = "br{}".format(self.brid_ord) - name = self.brid - - super(Bridge, self).__init__(name, unet.pid, aflags=unet.a_flags, logger=logger) - - self.logger.debug("Bridge: Creating") - - assert len(self.brid) <= 16 # Make sure fits in IFNAMSIZE - self.cmd_raises("ip link delete {} || true".format(self.brid)) - self.cmd_raises("ip link add {} type bridge".format(self.brid)) - self.cmd_raises("ip link set {} up".format(self.brid)) - - self.logger.debug("%s: Created, Running", self) - - def __str__(self): - return "Bridge({})".format(self.brid) - - def delete(self): - """Stop the bridge (i.e., delete the linux resources).""" - - rc, o, e = self.cmd_status("ip link show {}".format(self.brid), warn=False) - if not rc: - rc, o, e = self.cmd_status( - "ip link delete {}".format(self.brid), warn=False - ) - if rc: - self.logger.error( - "%s: error deleting bridge %s: %s", - self, - self.brid, - cmd_error(rc, o, e), - ) - else: - self.logger.debug("%s: Deleted.", self) - - -class Micronet(LinuxNamespace): # pylint: disable=R0205 - """ - Micronet. - """ - - def __init__(self): - """Create a Micronet.""" - - self.hosts = {} - self.switches = {} - self.links = {} - self.macs = {} - self.rmacs = {} - - super(Micronet, self).__init__("micronet", mount=True, net=True, uts=True) - - self.logger.debug("%s: Creating", self) - - def __str__(self): - return "Micronet()" - - def __getitem__(self, key): - if key in self.switches: - return self.switches[key] - return self.hosts[key] - - def add_host(self, name, cls=LinuxNamespace, **kwargs): - """Add a host to micronet.""" - - self.logger.debug("%s: add_host %s", self, name) - - self.hosts[name] = cls(name, **kwargs) - # Create a new mounted FS for tracking nested network namespaces creatd by the - # user with `ip netns add` - self.hosts[name].tmpfs_mount("/run/netns") - - def add_link(self, name1, name2, if1, if2): - """Add a link between switch and host to micronet.""" - isp2p = False - if name1 in self.switches: - assert name2 in self.hosts - elif name2 in self.switches: - assert name1 in self.hosts - name1, name2 = name2, name1 - if1, if2 = if2, if1 - else: - # p2p link - assert name1 in self.hosts - assert name2 in self.hosts - isp2p = True - - lname = "{}:{}-{}:{}".format(name1, if1, name2, if2) - self.logger.debug("%s: add_link %s%s", self, lname, " p2p" if isp2p else "") - self.links[lname] = (name1, if1, name2, if2) - - # And create the veth now. - if isp2p: - lhost, rhost = self.hosts[name1], self.hosts[name2] - lifname = "i1{:x}".format(lhost.pid) - rifname = "i2{:x}".format(rhost.pid) - self.cmd_raises( - "ip link add {} type veth peer name {}".format(lifname, rifname) - ) - - self.cmd_raises("ip link set {} netns {}".format(lifname, lhost.pid)) - lhost.cmd_raises("ip link set {} name {}".format(lifname, if1)) - lhost.cmd_raises("ip link set {} up".format(if1)) - lhost.register_interface(if1) - - self.cmd_raises("ip link set {} netns {}".format(rifname, rhost.pid)) - rhost.cmd_raises("ip link set {} name {}".format(rifname, if2)) - rhost.cmd_raises("ip link set {} up".format(if2)) - rhost.register_interface(if2) - else: - switch = self.switches[name1] - host = self.hosts[name2] - - assert len(if1) <= 16 and len(if2) <= 16 # Make sure fits in IFNAMSIZE - - self.logger.debug("%s: Creating veth pair for link %s", self, lname) - self.cmd_raises( - "ip link add {} type veth peer name {} netns {}".format( - if1, if2, host.pid - ) - ) - self.cmd_raises("ip link set {} netns {}".format(if1, switch.pid)) - switch.register_interface(if1) - host.register_interface(if2) - self.cmd_raises("ip link set {} master {}".format(if1, switch.brid)) - self.cmd_raises("ip link set {} up".format(if1)) - host.cmd_raises("ip link set {} up".format(if2)) - - # Cache the MAC values, and reverse mapping - self.get_mac(name1, if1) - self.get_mac(name2, if2) - - def add_switch(self, name): - """Add a switch to micronet.""" - - self.logger.debug("%s: add_switch %s", self, name) - self.switches[name] = Bridge(name, self) - - def get_mac(self, name, ifname): - if name in self.hosts: - dev = self.hosts[name] - else: - dev = self.switches[name] - - if (name, ifname) not in self.macs: - _, output, _ = dev.cmd_status("ip -o link show " + ifname) - m = re.match(".*link/(loopback|ether) ([0-9a-fA-F:]+) .*", output) - mac = m.group(2) - self.macs[(name, ifname)] = mac - self.rmacs[mac] = (name, ifname) - - return self.macs[(name, ifname)] - - def delete(self): - """Delete the micronet topology.""" - - self.logger.debug("%s: Deleting.", self) - - for lname, (_, _, rname, rif) in self.links.items(): - host = self.hosts[rname] - - self.logger.debug("%s: Deleting veth pair for link %s", self, lname) - - rc, o, e = host.cmd_status("ip link delete {}".format(rif), warn=False) - if rc: - self.logger.error( - "Error deleting veth pair %s: %s", lname, cmd_error(rc, o, e) - ) - - self.links = {} - - for host in self.hosts.values(): - try: - host.delete() - except Exception as error: - self.logger.error( - "%s: error while deleting host %s: %s", self, host, error - ) - - self.hosts = {} - - for switch in self.switches.values(): - try: - switch.delete() - except Exception as error: - self.logger.error( - "%s: error while deleting switch %s: %s", self, switch, error - ) - self.switches = {} - - self.logger.debug("%s: Deleted.", self) - - super(Micronet, self).delete() - - -# --------------------------- -# Root level utility function -# --------------------------- - - -def get_exec_path(binary): - base = Commander("base") - return base.get_exec_path(binary) - - -commander = Commander("micronet") +# flake8: noqa + +from munet.base import BaseMunet as Micronet +from munet.base import ( + Bridge, + Commander, + LinuxNamespace, + SharedNamespace, + Timeout, + cmd_error, + comm_error, + commander, + get_exec_path, + proc_error, + root_hostname, + shell_quote, +) diff --git a/tests/topotests/lib/micronet_cli.py b/tests/topotests/lib/micronet_cli.py deleted file mode 100644 index e54b75f710..0000000000 --- a/tests/topotests/lib/micronet_cli.py +++ /dev/null @@ -1,306 +0,0 @@ -# -*- coding: utf-8 eval: (blacken-mode 1) -*- -# SPDX-License-Identifier: GPL-2.0-or-later -# -# July 24 2021, Christian Hopps <chopps@labn.net> -# -# Copyright (c) 2021, LabN Consulting, L.L.C. -# -import argparse -import logging -import os -import pty -import re -import readline -import select -import socket -import subprocess -import sys -import tempfile -import termios -import tty - - -ENDMARKER = b"\x00END\x00" - - -def lineiter(sock): - s = "" - while True: - sb = sock.recv(256) - if not sb: - return - - s += sb.decode("utf-8") - i = s.find("\n") - if i != -1: - yield s[:i] - s = s[i + 1 :] - - -def spawn(unet, host, cmd): - if sys.stdin.isatty(): - old_tty = termios.tcgetattr(sys.stdin) - tty.setraw(sys.stdin.fileno()) - try: - master_fd, slave_fd = pty.openpty() - - # use os.setsid() make it run in a new process group, or bash job - # control will not be enabled - p = unet.hosts[host].popen( - cmd, - preexec_fn=os.setsid, - stdin=slave_fd, - stdout=slave_fd, - stderr=slave_fd, - universal_newlines=True, - ) - - while p.poll() is None: - r, w, e = select.select([sys.stdin, master_fd], [], [], 0.25) - if sys.stdin in r: - d = os.read(sys.stdin.fileno(), 10240) - os.write(master_fd, d) - elif master_fd in r: - o = os.read(master_fd, 10240) - if o: - os.write(sys.stdout.fileno(), o) - finally: - # restore tty settings back - if sys.stdin.isatty(): - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) - - -def doline(unet, line, writef): - def host_cmd_split(unet, cmd): - csplit = cmd.split() - for i, e in enumerate(csplit): - if e not in unet.hosts: - break - hosts = csplit[:i] - if not hosts: - hosts = sorted(unet.hosts.keys()) - cmd = " ".join(csplit[i:]) - return hosts, cmd - - line = line.strip() - m = re.match(r"^(\S+)(?:\s+(.*))?$", line) - if not m: - return True - - cmd = m.group(1) - oargs = m.group(2) if m.group(2) else "" - if cmd == "q" or cmd == "quit": - return False - if cmd == "hosts": - writef("%% hosts: %s\n" % " ".join(sorted(unet.hosts.keys()))) - elif cmd in ["term", "vtysh", "xterm"]: - args = oargs.split() - if not args or (len(args) == 1 and args[0] == "*"): - args = sorted(unet.hosts.keys()) - hosts = [unet.hosts[x] for x in args if x in unet.hosts] - for host in hosts: - if cmd == "t" or cmd == "term": - host.run_in_window("bash", title="sh-%s" % host) - elif cmd == "v" or cmd == "vtysh": - host.run_in_window("vtysh", title="vt-%s" % host) - elif cmd == "x" or cmd == "xterm": - host.run_in_window("bash", title="sh-%s" % host, forcex=True) - elif cmd == "sh": - hosts, cmd = host_cmd_split(unet, oargs) - for host in hosts: - if sys.stdin.isatty(): - spawn(unet, host, cmd) - else: - if len(hosts) > 1: - writef("------ Host: %s ------\n" % host) - output = unet.hosts[host].cmd_legacy(cmd) - writef(output) - if len(hosts) > 1: - writef("------- End: %s ------\n" % host) - writef("\n") - elif cmd == "h" or cmd == "help": - writef( - """ -Commands: - help :: this help - sh [hosts] <shell-command> :: execute <shell-command> on <host> - term [hosts] :: open shell terminals for hosts - vtysh [hosts] :: open vtysh terminals for hosts - [hosts] <vtysh-command> :: execute vtysh-command on hosts\n\n""" - ) - else: - hosts, cmd = host_cmd_split(unet, line) - for host in hosts: - if len(hosts) > 1: - writef("------ Host: %s ------\n" % host) - output = unet.hosts[host].cmd_legacy('vtysh -c "{}"'.format(cmd)) - writef(output) - if len(hosts) > 1: - writef("------- End: %s ------\n" % host) - writef("\n") - return True - - -def cli_server_setup(unet): - sockdir = tempfile.mkdtemp("-sockdir", "pyt") - sockpath = os.path.join(sockdir, "cli-server.sock") - try: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.settimeout(10) - sock.bind(sockpath) - sock.listen(1) - return sock, sockdir, sockpath - except Exception: - unet.cmd_status("rm -rf " + sockdir) - raise - - -def cli_server(unet, server_sock): - sock, addr = server_sock.accept() - - # Go into full non-blocking mode now - sock.settimeout(None) - - for line in lineiter(sock): - line = line.strip() - - def writef(x): - xb = x.encode("utf-8") - sock.send(xb) - - if not doline(unet, line, writef): - return - sock.send(ENDMARKER) - - -def cli_client(sockpath, prompt="unet> "): - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.settimeout(10) - sock.connect(sockpath) - - # Go into full non-blocking mode now - sock.settimeout(None) - - print("\n--- Micronet CLI Starting ---\n\n") - while True: - if sys.version_info[0] == 2: - line = raw_input(prompt) # pylint: disable=E0602 - else: - line = input(prompt) - if line is None: - return - - # Need to put \n back - line += "\n" - - # Send the CLI command - sock.send(line.encode("utf-8")) - - def bendswith(b, sentinel): - slen = len(sentinel) - return len(b) >= slen and b[-slen:] == sentinel - - # Collect the output - rb = b"" - while not bendswith(rb, ENDMARKER): - lb = sock.recv(4096) - if not lb: - return - rb += lb - - # Remove the marker - rb = rb[: -len(ENDMARKER)] - - # Write the output - sys.stdout.write(rb.decode("utf-8")) - - -def local_cli(unet, outf, prompt="unet> "): - print("\n--- Micronet CLI Starting ---\n\n") - while True: - if sys.version_info[0] == 2: - line = raw_input(prompt) # pylint: disable=E0602 - else: - line = input(prompt) - if line is None: - return - if not doline(unet, line, outf.write): - return - - -def cli( - unet, - histfile=None, - sockpath=None, - force_window=False, - title=None, - prompt=None, - background=True, -): - logger = logging.getLogger("cli-client") - - if prompt is None: - prompt = "unet> " - - if force_window or not sys.stdin.isatty(): - # Run CLI in another window b/c we have no tty. - sock, sockdir, sockpath = cli_server_setup(unet) - - python_path = unet.get_exec_path(["python3", "python"]) - us = os.path.realpath(__file__) - cmd = "{} {}".format(python_path, us) - if histfile: - cmd += " --histfile=" + histfile - if title: - cmd += " --prompt={}".format(title) - cmd += " " + sockpath - - try: - unet.run_in_window(cmd, new_window=True, title=title, background=background) - return cli_server(unet, sock) - finally: - unet.cmd_status("rm -rf " + sockdir) - - if not unet: - logger.debug("client-cli using sockpath %s", sockpath) - - try: - if histfile is None: - histfile = os.path.expanduser("~/.micronet-history.txt") - if not os.path.exists(histfile): - if unet: - unet.cmd("touch " + histfile) - else: - subprocess.run("touch " + histfile) - if histfile: - readline.read_history_file(histfile) - except Exception: - pass - - try: - if sockpath: - cli_client(sockpath, prompt=prompt) - else: - local_cli(unet, sys.stdout, prompt=prompt) - except EOFError: - pass - except Exception as ex: - logger.critical("cli: got exception: %s", ex, exc_info=True) - raise - finally: - readline.write_history_file(histfile) - - -if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG, filename="/tmp/topotests/cli-client.log") - logger = logging.getLogger("cli-client") - logger.info("Start logging cli-client") - - parser = argparse.ArgumentParser() - parser.add_argument("--histfile", help="file to user for history") - parser.add_argument("--prompt-text", help="prompt string to use") - parser.add_argument("socket", help="path to pair of sockets to communicate over") - args = parser.parse_args() - - prompt = "{}> ".format(args.prompt_text) if args.prompt_text else "unet> " - cli(None, args.histfile, args.socket, prompt=prompt) diff --git a/tests/topotests/lib/micronet_compat.py b/tests/topotests/lib/micronet_compat.py index 5a69c56d8d..c5c2adc545 100644 --- a/tests/topotests/lib/micronet_compat.py +++ b/tests/topotests/lib/micronet_compat.py @@ -3,140 +3,44 @@ # # July 11 2021, Christian Hopps <chopps@labn.net> # -# Copyright (c) 2021, LabN Consulting, L.L.C +# Copyright (c) 2021-2023, LabN Consulting, L.L.C # - -import glob -import logging +import ipaddress import os -import signal -import time - -from lib.micronet import LinuxNamespace, Micronet -from lib.micronet_cli import cli - - -def get_pids_with_env(has_var, has_val=None): - result = {} - for pidenv in glob.iglob("/proc/*/environ"): - pid = pidenv.split("/")[2] - try: - with open(pidenv, "rb") as rfb: - envlist = [ - x.decode("utf-8").split("=", 1) for x in rfb.read().split(b"\0") - ] - envlist = [[x[0], ""] if len(x) == 1 else x for x in envlist] - envdict = dict(envlist) - if has_var not in envdict: - continue - if has_val is None: - result[pid] = envdict - elif envdict[has_var] == str(has_val): - result[pid] = envdict - except Exception: - # E.g., process exited and files are gone - pass - return result - - -def _kill_piddict(pids_by_upid, sig): - for upid, pids in pids_by_upid: - logging.info( - "Sending %s to (%s) of micronet pid %s", sig, ", ".join(pids), upid - ) - for pid in pids: - try: - os.kill(int(pid), sig) - except Exception: - pass - - -def _get_our_pids(): - ourpid = str(os.getpid()) - piddict = get_pids_with_env("MICRONET_PID", ourpid) - pids = [x for x in piddict if x != ourpid] - if pids: - return {ourpid: pids} - return {} - - -def _get_other_pids(): - piddict = get_pids_with_env("MICRONET_PID") - unet_pids = {d["MICRONET_PID"] for d in piddict.values()} - pids_by_upid = {p: set() for p in unet_pids} - for pid, envdict in piddict.items(): - pids_by_upid[envdict["MICRONET_PID"]].add(pid) - # Filter out any child pid sets whos micronet pid is still running - return {x: y for x, y in pids_by_upid.items() if x not in y} - - -def _get_pids_by_upid(ours): - if ours: - return _get_our_pids() - return _get_other_pids() - - -def _cleanup_pids(ours): - pids_by_upid = _get_pids_by_upid(ours).items() - if not pids_by_upid: - return - - _kill_piddict(pids_by_upid, signal.SIGTERM) - - # Give them 5 second to exit cleanly - logging.info("Waiting up to 5s to allow for clean exit of abandon'd pids") - for _ in range(0, 5): - pids_by_upid = _get_pids_by_upid(ours).items() - if not pids_by_upid: - return - time.sleep(1) - - pids_by_upid = _get_pids_by_upid(ours).items() - _kill_piddict(pids_by_upid, signal.SIGKILL) - - -def cleanup_current(): - """Attempt to cleanup preview runs. - - Currently this only scans for old processes. - """ - logging.info("reaping current micronet processes") - _cleanup_pids(True) - -def cleanup_previous(): - """Attempt to cleanup preview runs. - - Currently this only scans for old processes. - """ - logging.info("reaping past micronet processes") - _cleanup_pids(False) +from munet import cli +from munet.base import BaseMunet, LinuxNamespace class Node(LinuxNamespace): """Node (mininet compat).""" - def __init__(self, name, **kwargs): - """ - Create a Node. - """ - self.params = kwargs + def __init__(self, name, rundir=None, **kwargs): + nkwargs = {} + if "unet" in kwargs: + nkwargs["unet"] = kwargs["unet"] if "private_mounts" in kwargs: - private_mounts = kwargs["private_mounts"] - else: - private_mounts = kwargs.get("privateDirs", []) + nkwargs["private_mounts"] = kwargs["private_mounts"] + if "logger" in kwargs: + nkwargs["logger"] = kwargs["logger"] - logger = kwargs.get("logger") + # This is expected by newer munet CLI code + self.config_dirname = "" + self.config = {"kind": "frr"} + self.mgmt_ip = None + self.mgmt_ip6 = None - super(Node, self).__init__(name, logger=logger, private_mounts=private_mounts) + super().__init__(name, **nkwargs) + + self.rundir = self.unet.rundir.joinpath(self.name) def cmd(self, cmd, **kwargs): """Execute a command, joins stdout, stderr, ignores exit status.""" return super(Node, self).cmd_legacy(cmd, **kwargs) - def config(self, lo="up", **params): + def config_host(self, lo="up", **params): """Called by Micronet when topology is built (but not started).""" # mininet brings up loopback here. del params @@ -148,25 +52,79 @@ class Node(LinuxNamespace): def terminate(self): return + def add_vlan(self, vlanname, linkiface, vlanid): + self.logger.debug("Adding VLAN interface: %s (%s)", vlanname, vlanid) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises( + [ + ip_path, + "link", + "add", + "link", + linkiface, + "name", + vlanname, + "type", + "vlan", + "id", + vlanid, + ] + ) + self.cmd_raises([ip_path, "link", "set", "dev", vlanname, "up"]) + + def add_loop(self, loopname): + self.logger.debug("Adding Linux iface: %s", loopname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises([ip_path, "link", "add", loopname, "type", "dummy"]) + self.cmd_raises([ip_path, "link", "set", "dev", loopname, "up"]) + + def add_l3vrf(self, vrfname, tableid): + self.logger.debug("Adding Linux VRF: %s", vrfname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises( + [ip_path, "link", "add", vrfname, "type", "vrf", "table", tableid] + ) + self.cmd_raises([ip_path, "link", "set", "dev", vrfname, "up"]) + + def del_iface(self, iface): + self.logger.debug("Removing Linux Iface: %s", iface) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises([ip_path, "link", "del", iface]) + + def attach_iface_to_l3vrf(self, ifacename, vrfname): + self.logger.debug("Attaching Iface %s to Linux VRF %s", ifacename, vrfname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + if vrfname: + self.cmd_raises( + [ip_path, "link", "set", "dev", ifacename, "master", vrfname] + ) + else: + self.cmd_raises([ip_path, "link", "set", "dev", ifacename, "nomaster"]) + + set_cwd = LinuxNamespace.set_ns_cwd + class Topo(object): # pylint: disable=R0205 def __init__(self, *args, **kwargs): raise Exception("Remove Me") -class Mininet(Micronet): +class Mininet(BaseMunet): """ Mininet using Micronet. """ g_mnet_inst = None - def __init__(self, controller=None): + def __init__(self, rundir=None, pytestconfig=None): """ Create a Micronet. """ - assert not controller - if Mininet.g_mnet_inst is not None: Mininet.g_mnet_inst.stop() Mininet.g_mnet_inst = self @@ -181,7 +139,145 @@ class Mininet(Micronet): # to set permissions to root:frr 770 to make this unneeded in that case # os.umask(0) - super(Mininet, self).__init__() + super(Mininet, self).__init__( + pid=False, rundir=rundir, pytestconfig=pytestconfig + ) + + # From munet/munet/native.py + with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f: + f.write(f"{self.pid}\n") + + with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f: + f.write(f'{" ".join([str(x) for x in self.pids])}\n') + + hosts_file = os.path.join(self.rundir, "hosts.txt") + with open(hosts_file, "w", encoding="ascii") as hf: + hf.write( + f"""127.0.0.1\tlocalhost {self.name} +::1\tip6-localhost ip6-loopback +fe00::0\tip6-localnet +ff00::0\tip6-mcastprefix +ff02::1\tip6-allnodes +ff02::2\tip6-allrouters +""" + ) + self.bind_mount(hosts_file, "/etc/hosts") + + # Common CLI commands for any topology + cdict = { + "commands": [ + # + # Window commands. + # + { + "name": "pcap", + "format": "pcap NETWORK", + "help": ( + "capture packets from NETWORK into file capture-NETWORK.pcap" + " the command is run within a new window which also shows" + " packet summaries. NETWORK can also be an interface specified" + " as HOST:INTF. To capture inside the host namespace." + ), + "exec": "tshark -s 9200 -i {0} -P -w capture-{0}.pcap", + "top-level": True, + "new-window": {"background": True}, + }, + { + "name": "term", + "format": "term HOST [HOST ...]", + "help": "open terminal[s] (TMUX or XTerm) on HOST[S], * for all", + "exec": "bash", + "new-window": True, + }, + { + "name": "vtysh", + "exec": "/usr/bin/vtysh", + "format": "vtysh ROUTER [ROUTER ...]", + "new-window": True, + "kinds": ["frr"], + }, + { + "name": "xterm", + "format": "xterm HOST [HOST ...]", + "help": "open XTerm[s] on HOST[S], * for all", + "exec": "bash", + "new-window": { + "forcex": True, + }, + }, + { + "name": "logd", + "exec": "tail -F %RUNDIR%/{}.log", + "format": "logd HOST [HOST ...] DAEMON", + "help": ( + "tail -f on the logfile of the given " + "DAEMON for the given HOST[S]" + ), + "new-window": True, + }, + { + "name": "stdlog", + "exec": ( + "[ -e %RUNDIR%/frr.log ] && tail -F %RUNDIR%/frr.log " + "|| tail -F /var/log/frr.log" + ), + "format": "stdlog HOST [HOST ...]", + "help": "tail -f on the `frr.log` for the given HOST[S]", + "new-window": True, + }, + { + "name": "stdout", + "exec": "tail -F %RUNDIR%/{0}.err", + "format": "stdout HOST [HOST ...] DAEMON", + "help": ( + "tail -f on the stdout of the given DAEMON for the given HOST[S]" + ), + "new-window": True, + }, + { + "name": "stderr", + "exec": "tail -F %RUNDIR%/{0}.out", + "format": "stderr HOST [HOST ...] DAEMON", + "help": ( + "tail -f on the stderr of the given DAEMON for the given HOST[S]" + ), + "new-window": True, + }, + # + # Non-window commands. + # + { + "name": "", + "exec": "vtysh -c '{}'", + "format": "[ROUTER ...] COMMAND", + "help": "execute vtysh COMMAND on the router[s]", + "kinds": ["frr"], + }, + { + "name": "sh", + "format": "[HOST ...] sh <SHELL-COMMAND>", + "help": "execute <SHELL-COMMAND> on hosts", + "exec": "{}", + }, + { + "name": "shi", + "format": "[HOST ...] shi <INTERACTIVE-COMMAND>", + "help": "execute <INTERACTIVE-COMMAND> on HOST[s]", + "exec": "{}", + "interactive": True, + }, + ] + } + + cli.add_cli_config(self, cdict) + + shellopt = self.cfgopt.get_option_list("--shell") + if "all" in shellopt or "." in shellopt: + self.run_in_window("bash") + + # This is expected by newer munet CLI code + self.config_dirname = "" + self.config = {} self.logger.debug("%s: Creating", self) @@ -219,12 +315,15 @@ class Mininet(Micronet): host.cmd_raises("ip addr add {}/{} dev {}".format(ip, plen, first_intf)) + # can be used by munet cli + host.mgmt_ip = ipaddress.ip_address(ip) + if "defaultRoute" in params: host.cmd_raises( "ip route add default {}".format(params["defaultRoute"]) ) - host.config() + host.config_host() self.configured_hosts.add(name) @@ -236,6 +335,24 @@ class Mininet(Micronet): def start(self): """Start the micronet topology.""" + pcapopt = self.cfgopt.get_option_list("--pcap") + if "all" in pcapopt: + pcapopt = self.switches.keys() + for pcap in pcapopt: + if ":" in pcap: + host, intf = pcap.split(":") + pcap = f"{host}-{intf}" + host = self.hosts[host] + else: + host = self + intf = pcap + pcapfile = f"{self.rundir}/capture-{pcap}.pcap" + host.run_in_window( + f"tshark -s 9200 -i {intf} -P -w {pcapfile}", + background=True, + title=f"cap:{pcap}", + ) + self.logger.debug("%s: Starting (no-op).", self) def stop(self): @@ -250,4 +367,4 @@ class Mininet(Micronet): Mininet.g_mnet_inst = None def cli(self): - cli(self) + cli.cli(self) diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index 23b1f2e533..dad87440bc 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -577,15 +577,15 @@ def verify_ospf_neighbor( "ospf": { "neighbors": { "r1": { - "state": "Full", + "nbrState": "Full", "role": "DR" }, "r2": { - "state": "Full", + "nbrState": "Full", "role": "DROther" }, "r3": { - "state": "Full", + "nbrState": "Full", "role": "DROther" } } @@ -642,13 +642,13 @@ def verify_ospf_neighbor( neighbor_ip = neighbor_ip.lower() nbr_rid = data_rid try: - nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0] - intf_state = show_ospf_json[nbr_rid][0]["state"].split("/")[1] + nh_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[0] + intf_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[1] except KeyError: errormsg = "[DUT: {}] OSPF peer {} missing".format(router, nbr_rid) return errormsg - nbr_state = nbr_data.setdefault("state", None) + nbr_state = nbr_data.setdefault("nbrState", None) nbr_role = nbr_data.setdefault("role", None) if nbr_state: @@ -724,8 +724,9 @@ def verify_ospf_neighbor( nh_state = None neighbor_ip = neighbor_ip.lower() nbr_rid = data_rid + try: - nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0] + nh_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[0] except KeyError: errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format( router, nbr_rid, ospf_nbr @@ -2477,7 +2478,7 @@ def verify_ospf_gr_helper(tgen, topo, dut, input_dict=None): input_dict = { "helperSupport":"Disabled", "strictLsaCheck":"Enabled", - "restartSupoort":"Planned and Unplanned Restarts", + "restartSupport":"Planned and Unplanned Restarts", "supportedGracePeriod":1800 } result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py index 6878d93f37..925890b324 100644 --- a/tests/topotests/lib/pim.py +++ b/tests/topotests/lib/pim.py @@ -5120,6 +5120,75 @@ def verify_pim6_config(tgen, input_dict, expected=True): logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True + +@retry(retry_timeout=62) +def verify_local_mld_groups(tgen, dut, interface, group_addresses): + """ + Verify local MLD groups are received from an intended interface + by running "show ipv6 mld join json" command + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + * `interface`: interface, from which IGMP groups are configured + * `group_addresses`: MLD group address + Usage + ----- + dut = "r1" + interface = "r1-r0-eth0" + group_address = "ffaa::1" + result = verify_local_mld_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 local MLD groups received:", dut) + show_ipv6_local_mld_json = run_frr_cmd( + rnode, "show ipv6 mld join json", isjson=True + ) + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + if interface not in show_ipv6_local_mld_json["default"]: + + errormsg = ( + "[DUT %s]: Verifying local MLD group received" + " from interface %s [FAILED]!! " % (dut, interface) + ) + return errormsg + + for grp_addr in group_addresses: + found = False + if grp_addr in show_ipv6_local_mld_json["default"][interface]: + found = True + break + if not found: + errormsg = ( + "[DUT %s]: Verifying local MLD group received" + " from interface %s [FAILED]!! " + " Expected: %s " % (dut, interface, grp_addr) + ) + return errormsg + + logger.info( + "[DUT %s]: Verifying local MLD 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 + # def cleanup(self): # super(McastTesterHelper, self).cleanup() diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 41da660b7d..2d6138990e 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -25,6 +25,7 @@ Basic usage instructions: * After running stop Mininet with: tgen.stop_topology() """ +import configparser import grp import inspect import json @@ -33,20 +34,16 @@ import os import platform import pwd import re +import shlex import subprocess import sys from collections import OrderedDict -if sys.version_info[0] > 2: - import configparser -else: - import ConfigParser as configparser - import lib.topolog as topolog from lib.micronet import Commander from lib.micronet_compat import Mininet from lib.topolog import logger -from lib.topotest import g_extra_config +from munet.testing.util import pause_test from lib import topotest @@ -192,7 +189,7 @@ class Topogen(object): self._load_config() # Create new log directory - self.logdir = topotest.get_logs_path(g_extra_config["rundir"]) + self.logdir = topotest.get_logs_path(topotest.g_pytest_config.option.rundir) subprocess.check_call( "mkdir -p {0} && chmod 1777 {0}".format(self.logdir), shell=True ) @@ -212,7 +209,10 @@ class Topogen(object): # Mininet(Micronet) to build the actual topology. assert not inspect.isclass(topodef) - self.net = Mininet(controller=None) + self.net = Mininet(rundir=self.logdir, pytestconfig=topotest.g_pytest_config) + + # Adjust the parent namespace + topotest.fix_netns_limits(self.net) # New direct way: Either a dictionary defines the topology or a build function # is supplied, or a json filename all of which build the topology by calling @@ -236,7 +236,6 @@ class Topogen(object): self.add_topology_from_dict(topodef) def add_topology_from_dict(self, topodef): - keylist = ( topodef.keys() if isinstance(topodef, OrderedDict) @@ -451,7 +450,18 @@ class Topogen(object): first is a simple kill with no sleep, the second will sleep if not killed and try with a different signal. """ + pause = bool(self.net.cfgopt.get_option("--pause-at-end")) + pause = pause or bool(self.net.cfgopt.get_option("--pause")) + if pause: + try: + pause_test("Before MUNET delete") + except KeyboardInterrupt: + print("^C...continuing") + except Exception as error: + self.logger.error("\n...continuing after error: %s", error) + logger.info("stopping topology: {}".format(self.modname)) + errors = "" for gear in self.gears.values(): errors += gear.stop() @@ -504,7 +514,7 @@ class Topogen(object): def set_error(self, message, code=None): "Sets an error message and signal other tests to skip." - logger.info(message) + logger.info("setting error msg: %s", message) # If no code is defined use a sequential number if code is None: @@ -749,8 +759,8 @@ class TopoRouter(TopoGear): """ super(TopoRouter, self).__init__(tgen, name, **params) self.routertype = params.get("routertype", "frr") - if "privateDirs" not in params: - params["privateDirs"] = self.PRIVATE_DIRS + if "private_mounts" not in params: + params["private_mounts"] = self.PRIVATE_DIRS # Propagate the router log directory logfile = self._setup_tmpdir() @@ -799,7 +809,7 @@ class TopoRouter(TopoGear): grep_cmd = "grep 'ip {}' {}".format(daemonstr, source) else: grep_cmd = "grep 'router {}' {}".format(daemonstr, source) - result = self.run(grep_cmd).strip() + result = self.run(grep_cmd, warn=False).strip() if result: self.load_config(daemon) else: @@ -822,7 +832,7 @@ class TopoRouter(TopoGear): all routers. """ daemonstr = self.RD.get(daemon) - self.logger.info('loading "{}" configuration: {}'.format(daemonstr, source)) + self.logger.debug('loading "{}" configuration: {}'.format(daemonstr, source)) self.net.loadConf(daemonstr, source, param) def check_router_running(self): @@ -858,7 +868,7 @@ class TopoRouter(TopoGear): "conf t", "log file {}.log debug".format(daemon), "log commands", - "log timestamp precision 3", + "log timestamp precision 6", ] ), daemon=daemon, @@ -908,7 +918,7 @@ class TopoRouter(TopoGear): "conf t", "log file {}.log debug".format(daemon), "log commands", - "log timestamp precision 3", + "log timestamp precision 6", ] ), daemon=daemon, @@ -943,18 +953,20 @@ class TopoRouter(TopoGear): if daemon is not None: dparam += "-d {}".format(daemon) - vtysh_command = 'vtysh {} -c "{}" 2>/dev/null'.format(dparam, command) + vtysh_command = "vtysh {} -c {} 2>/dev/null".format( + dparam, shlex.quote(command) + ) - self.logger.info('vtysh command => "{}"'.format(command)) + self.logger.debug("vtysh command => {}".format(shlex.quote(command))) output = self.run(vtysh_command) dbgout = output.strip() if dbgout: if "\n" in dbgout: dbgout = dbgout.replace("\n", "\n\t") - self.logger.info("vtysh result:\n\t{}".format(dbgout)) + self.logger.debug("vtysh result:\n\t{}".format(dbgout)) else: - self.logger.info('vtysh result: "{}"'.format(dbgout)) + self.logger.debug('vtysh result: "{}"'.format(dbgout)) if isjson is False: return output @@ -994,7 +1006,7 @@ class TopoRouter(TopoGear): dbgcmds = commands if is_string(commands) else "\n".join(commands) dbgcmds = "\t" + dbgcmds.replace("\n", "\n\t") - self.logger.info("vtysh command => FILE:\n{}".format(dbgcmds)) + self.logger.debug("vtysh command => FILE:\n{}".format(dbgcmds)) res = self.run(vtysh_command) os.unlink(fname) @@ -1003,9 +1015,9 @@ class TopoRouter(TopoGear): if dbgres: if "\n" in dbgres: dbgres = dbgres.replace("\n", "\n\t") - self.logger.info("vtysh result:\n\t{}".format(dbgres)) + self.logger.debug("vtysh result:\n\t{}".format(dbgres)) else: - self.logger.info('vtysh result: "{}"'.format(dbgres)) + self.logger.debug('vtysh result: "{}"'.format(dbgres)) return res def report_memory_leaks(self, testname): @@ -1097,7 +1109,7 @@ class TopoHost(TopoGear): * `ip`: the IP address (string) for the host interface * `defaultRoute`: the default route that will be installed (e.g. 'via 10.0.0.1') - * `privateDirs`: directories that will be mounted on a different domain + * `private_mounts`: directories that will be mounted on a different domain (e.g. '/etc/important_dir'). """ super(TopoHost, self).__init__(tgen, name, **params) @@ -1117,10 +1129,10 @@ class TopoHost(TopoGear): def __str__(self): gear = super(TopoHost, self).__str__() - gear += ' TopoHost<ip="{}",defaultRoute="{}",privateDirs="{}">'.format( + gear += ' TopoHost<ip="{}",defaultRoute="{}",private_mounts="{}">'.format( self.params["ip"], self.params["defaultRoute"], - str(self.params["privateDirs"]), + str(self.params["private_mounts"]), ) return gear @@ -1143,10 +1155,10 @@ class TopoExaBGP(TopoHost): (e.g. 'via 10.0.0.1') Note: the different between a host and a ExaBGP peer is that this class - has a privateDirs already defined and contains functions to handle ExaBGP - things. + has a private_mounts already defined and contains functions to handle + ExaBGP things. """ - params["privateDirs"] = self.PRIVATE_DIRS + params["private_mounts"] = self.PRIVATE_DIRS super(TopoExaBGP, self).__init__(tgen, name, **params) def __str__(self): @@ -1191,6 +1203,7 @@ class TopoExaBGP(TopoHost): # Diagnostic function # + # Disable linter branch warning. It is expected to have these here. # pylint: disable=R0912 def diagnose_env_linux(rundir): diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index d35b908e12..967f09ecbd 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -9,13 +9,13 @@ # Network Device Education Foundation, Inc. ("NetDEF") # +import configparser import difflib import errno import functools import glob import json import os -import pdb import platform import re import resource @@ -24,22 +24,17 @@ import subprocess import sys import tempfile import time +from collections.abc import Mapping from copy import deepcopy import lib.topolog as topolog +from lib.micronet_compat import Node from lib.topolog import logger - -if sys.version_info[0] > 2: - import configparser - from collections.abc import Mapping -else: - import ConfigParser as configparser - from collections import Mapping +from munet.base import Timeout from lib import micronet -from lib.micronet_compat import Node -g_extra_config = {} +g_pytest_config = None def get_logs_path(rundir): @@ -352,7 +347,7 @@ def run_and_expect(func, what, count=20, wait=3): count, wait ) - logger.info( + logger.debug( "'{}' polling started (interval {} secs, maximum {} tries)".format( func_name, wait, count ) @@ -366,7 +361,7 @@ def run_and_expect(func, what, count=20, wait=3): continue end_time = time.time() - logger.info( + logger.debug( "'{}' succeeded after {:.2f} seconds".format( func_name, end_time - start_time ) @@ -409,7 +404,7 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None): count, wait ) - logger.info( + logger.debug( "'{}' polling started (interval {} secs, maximum wait {} secs)".format( func_name, wait, int(wait * count) ) @@ -432,7 +427,7 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None): continue end_time = time.time() - logger.info( + logger.debug( "'{}' succeeded after {:.2f} seconds".format( func_name, end_time - start_time ) @@ -474,32 +469,6 @@ def int2dpid(dpid): ) -def pid_exists(pid): - "Check whether pid exists in the current process table." - - if pid <= 0: - return False - try: - os.waitpid(pid, os.WNOHANG) - except: - pass - try: - os.kill(pid, 0) - except OSError as err: - if err.errno == errno.ESRCH: - # ESRCH == No such process - return False - elif err.errno == errno.EPERM: - # EPERM clearly means there's a process to deny access to - return True - else: - # According to "man 2 kill" possible error values are - # (EINVAL, EPERM, ESRCH) - raise - else: - return True - - def get_textdiff(text1, text2, title1="", title2="", **opts): "Returns empty string if same or formatted diff" @@ -1086,7 +1055,7 @@ 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.*" + filepattern = logdir + "/" + router + ".asan." + component + ".*" logger.debug( "Log check for %s on %s, pattern %s\n" % (component, router, filepattern) ) @@ -1130,7 +1099,7 @@ def _sysctl_atleast(commander, variable, min_value): valstr = " ".join([str(x) for x in min_value]) else: valstr = str(min_value) - logger.info("Increasing sysctl %s from %s to %s", variable, cur_val, valstr) + logger.debug("Increasing sysctl %s from %s to %s", variable, cur_val, valstr) commander.cmd_raises('sysctl -w {}="{}"\n'.format(variable, valstr)) @@ -1161,7 +1130,7 @@ def _sysctl_assure(commander, variable, value): valstr = " ".join([str(x) for x in value]) else: valstr = str(value) - logger.info("Changing sysctl %s from %s to %s", variable, cur_val, valstr) + logger.debug("Changing sysctl %s from %s to %s", variable, cur_val, valstr) commander.cmd_raises('sysctl -w {}="{}"\n'.format(variable, valstr)) @@ -1204,7 +1173,7 @@ def rlimit_atleast(rname, min_value, raises=False): soft, hard = cval if soft < min_value: nval = (min_value, hard if min_value < hard else min_value) - logger.info("Increasing rlimit %s from %s to %s", rname, cval, nval) + logger.debug("Increasing rlimit %s from %s to %s", rname, cval, nval) resource.setrlimit(rname, nval) except subprocess.CalledProcessError as error: logger.warning( @@ -1215,10 +1184,9 @@ def rlimit_atleast(rname, min_value, raises=False): def fix_netns_limits(ns): - # Maximum read and write socket buffer sizes - sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2 ** 20]) - sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2 ** 20]) + sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2**20]) + sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2**20]) sysctl_assure(ns, "net.ipv4.conf.all.rp_filter", 0) sysctl_assure(ns, "net.ipv4.conf.default.rp_filter", 0) @@ -1277,8 +1245,8 @@ def fix_host_limits(): sysctl_atleast(None, "net.core.netdev_max_backlog", 4 * 1024) # Maximum read and write socket buffer sizes - sysctl_atleast(None, "net.core.rmem_max", 16 * 2 ** 20) - sysctl_atleast(None, "net.core.wmem_max", 16 * 2 ** 20) + sysctl_atleast(None, "net.core.rmem_max", 16 * 2**20) + sysctl_atleast(None, "net.core.wmem_max", 16 * 2**20) # Garbage Collection Settings for ARP and Neighbors sysctl_atleast(None, "net.ipv4.neigh.default.gc_thresh2", 4 * 1024) @@ -1303,7 +1271,8 @@ def fix_host_limits(): def setup_node_tmpdir(logdir, name): # Cleanup old log, valgrind, and core files. subprocess.check_call( - "rm -rf {0}/{1}.valgrind.* {1}.*.asan {0}/{1}/".format(logdir, name), shell=True + "rm -rf {0}/{1}.valgrind.* {0}/{1}.asan.* {0}/{1}/".format(logdir, name), + shell=True, ) # Setup the per node directory. @@ -1318,8 +1287,7 @@ def setup_node_tmpdir(logdir, name): class Router(Node): "A Node with IPv4/IPv6 forwarding enabled" - def __init__(self, name, **params): - + def __init__(self, name, *posargs, **params): # Backward compatibility: # Load configuration defaults like topogen. self.config_defaults = configparser.ConfigParser( @@ -1335,11 +1303,13 @@ class Router(Node): os.path.join(os.path.dirname(os.path.realpath(__file__)), "../pytest.ini") ) + self.perf_daemons = {} + # If this topology is using old API and doesn't have logdir # specified, then attempt to generate an unique logdir. self.logdir = params.get("logdir") if self.logdir is None: - self.logdir = get_logs_path(g_extra_config["rundir"]) + self.logdir = get_logs_path(g_pytest_config.getoption("--rundir")) if not params.get("logger"): # If logger is present topogen has already set this up @@ -1347,7 +1317,7 @@ class Router(Node): l = topolog.get_logger(name, log_level="debug", target=logfile) params["logger"] = l - super(Router, self).__init__(name, **params) + super(Router, self).__init__(name, *posargs, **params) self.daemondir = None self.hasmpls = False @@ -1407,8 +1377,8 @@ class Router(Node): # pylint: disable=W0221 # Some params are only meaningful for the parent class. - def config(self, **params): - super(Router, self).config(**params) + def config_host(self, **params): + super(Router, self).config_host(**params) # User did not specify the daemons directory, try to autodetect it. self.daemondir = params.get("daemondir") @@ -1478,11 +1448,11 @@ class Router(Node): logger.info("%s: stopping %s", self.name, ", ".join([x[0] for x in running])) for name, pid in running: - logger.info("{}: sending SIGTERM to {}".format(self.name, name)) + logger.debug("{}: sending SIGTERM to {}".format(self.name, name)) try: os.kill(pid, signal.SIGTERM) except OSError as err: - logger.info( + logger.debug( "%s: could not kill %s (%s): %s", self.name, name, pid, str(err) ) @@ -1526,10 +1496,13 @@ class Router(Node): def removeIPs(self): for interface in self.intfNames(): try: - self.intf_ip_cmd(interface, "ip address flush " + interface) + self.intf_ip_cmd(interface, "ip -4 address flush " + interface) + self.intf_ip_cmd( + interface, "ip -6 address flush " + interface + " scope global" + ) except Exception as ex: logger.error("%s can't remove IPs %s", self, str(ex)) - # pdb.set_trace() + # breakpoint() # assert False, "can't remove IPs %s" % str(ex) def checkCapability(self, daemon, param): @@ -1560,7 +1533,7 @@ class Router(Node): router_relative = os.path.join(script_dir, self.name, tail) if self.path_exists(router_relative): source = router_relative - self.logger.info( + self.logger.debug( "using router relative configuration: {}".format(source) ) @@ -1595,10 +1568,7 @@ class Router(Node): if (daemon == "zebra") and (self.daemons["mgmtd"] == 0): # Add mgmtd with zebra - if it exists - try: - mgmtd_path = os.path.join(self.daemondir, "mgmtd") - except: - pdb.set_trace() + mgmtd_path = os.path.join(self.daemondir, "mgmtd") if os.path.isfile(mgmtd_path): self.daemons["mgmtd"] = 1 self.daemons_options["mgmtd"] = "" @@ -1606,18 +1576,14 @@ class Router(Node): if (daemon == "zebra") and (self.daemons["staticd"] == 0): # Add staticd with zebra - if it exists - try: - staticd_path = os.path.join(self.daemondir, "staticd") - except: - pdb.set_trace() - + staticd_path = os.path.join(self.daemondir, "staticd") if os.path.isfile(staticd_path): self.daemons["staticd"] = 1 self.daemons_options["staticd"] = "" # Auto-Started staticd has no config, so it will read from zebra config else: - logger.info("No daemon {} known".format(daemon)) + logger.warning("No daemon {} known".format(daemon)) # print "Daemons after:", self.daemons def runInWindow(self, cmd, title=None): @@ -1685,8 +1651,7 @@ class Router(Node): # used self.cmd("echo 100000 > /proc/sys/net/mpls/platform_labels") - shell_routers = g_extra_config["shell"] - if "all" in shell_routers or self.name in shell_routers: + if g_pytest_config.name_in_option_list(self.name, "--shell"): self.run_in_window(os.getenv("SHELL", "bash"), title="sh-%s" % self.name) if self.daemons["eigrpd"] == 1: @@ -1703,8 +1668,7 @@ class Router(Node): status = self.startRouterDaemons(tgen=tgen) - vtysh_routers = g_extra_config["vtysh"] - if "all" in vtysh_routers or self.name in vtysh_routers: + if g_pytest_config.name_in_option_list(self.name, "--vtysh"): self.run_in_window("vtysh", title="vt-%s" % self.name) if self.unified_config: @@ -1724,13 +1688,13 @@ class Router(Node): def startRouterDaemons(self, daemons=None, tgen=None): "Starts FRR daemons for this router." - asan_abort = g_extra_config["asan_abort"] - gdb_breakpoints = g_extra_config["gdb_breakpoints"] - gdb_daemons = g_extra_config["gdb_daemons"] - gdb_routers = g_extra_config["gdb_routers"] - valgrind_extra = g_extra_config["valgrind_extra"] - valgrind_memleaks = g_extra_config["valgrind_memleaks"] - strace_daemons = g_extra_config["strace_daemons"] + asan_abort = bool(g_pytest_config.option.asan_abort) + gdb_breakpoints = g_pytest_config.get_option_list("--gdb-breakpoints") + gdb_daemons = g_pytest_config.get_option_list("--gdb-daemons") + gdb_routers = g_pytest_config.get_option_list("--gdb-routers") + valgrind_extra = bool(g_pytest_config.option.valgrind_extra) + valgrind_memleaks = bool(g_pytest_config.option.valgrind_memleaks) + strace_daemons = g_pytest_config.get_option_list("--strace-daemons") # Get global bundle data if not self.path_exists("/etc/frr/support_bundle_commands.conf"): @@ -1754,11 +1718,31 @@ class Router(Node): self.reportCores = True # XXX: glue code forward ported from removed function. - if self.version == None: + if self.version is None: self.version = self.cmd( os.path.join(self.daemondir, "bgpd") + " -v" ).split()[2] logger.info("{}: running version: {}".format(self.name, self.version)) + + perfds = {} + perf_options = g_pytest_config.get_option("--perf-options", "-g") + for perf in g_pytest_config.get_option("--perf", []): + if "," in perf: + daemon, routers = perf.split(",", 1) + perfds[daemon] = routers.split(",") + else: + daemon = perf + perfds[daemon] = ["all"] + + logd_options = {} + for logd in g_pytest_config.get_option("--logd", []): + if "," in logd: + daemon, routers = logd.split(",", 1) + logd_options[daemon] = routers.split(",") + else: + daemon = logd + logd_options[daemon] = ["all"] + # If `daemons` was specified then some upper API called us with # specific daemons, otherwise just use our own configuration. daemons_list = [] @@ -1770,22 +1754,36 @@ class Router(Node): if self.daemons[daemon] == 1: daemons_list.append(daemon) + tail_log_files = [] + check_daemon_files = [] + def start_daemon(daemon, extra_opts=None): daemon_opts = self.daemons_options.get(daemon, "") + + # get pid and vty filenames and remove the files + m = re.match(r"(.* |^)-n (\d+)( ?.*|$)", daemon_opts) + dfname = daemon if not m else "{}-{}".format(daemon, m.group(2)) + runbase = "/var/run/{}/{}".format(self.routertype, dfname) + # If this is a new system bring-up remove the pid/vty files, otherwise + # do not since apparently presence of the pidfile impacts BGP GR + self.cmd_status("rm -f {0}.pid {0}.vty".format(runbase)) + rediropt = " > {0}.out 2> {0}.err".format(daemon) if daemon == "snmpd": binary = "/usr/sbin/snmpd" cmdenv = "" cmdopt = "{} -C -c /etc/frr/snmpd.conf -p ".format( daemon_opts - ) + "/var/run/{}/snmpd.pid -x /etc/frr/agentx".format(self.routertype) + ) + "{}.pid -x /etc/frr/agentx".format(runbase) + # check_daemon_files.append(runbase + ".pid") else: binary = os.path.join(self.daemondir, daemon) + check_daemon_files.extend([runbase + ".pid", runbase + ".vty"]) cmdenv = "ASAN_OPTIONS=" if asan_abort: - cmdenv = "abort_on_error=1:" - cmdenv += "log_path={0}/{1}.{2}.asan ".format( + cmdenv += "abort_on_error=1:" + cmdenv += "log_path={0}/{1}.asan.{2} ".format( self.logdir, self.name, daemon ) @@ -1808,9 +1806,15 @@ class Router(Node): daemon, self.logdir, self.name ) - cmdopt = "{} --command-log-always --log file:{}.log --log-level debug".format( - daemon_opts, daemon - ) + cmdopt = "{} --command-log-always ".format(daemon_opts) + cmdopt += "--log file:{}.log --log-level debug".format(daemon) + + if daemon in logd_options: + logdopt = logd_options[daemon] + if "all" in logdopt or self.name in logdopt: + tail_log_files.append( + "{}/{}/{}.log".format(self.logdir, self.name, daemon) + ) if extra_opts: cmdopt += " " + extra_opts @@ -1837,6 +1841,23 @@ class Router(Node): logger.info( "%s: %s %s launched in gdb window", self, self.routertype, daemon ) + elif daemon in perfds and (self.name in perfds[daemon] or "all" in perfds[daemon]): + cmdopt += rediropt + cmd = " ".join(["perf record {} --".format(perf_options), binary, cmdopt]) + p = self.popen(cmd) + self.perf_daemons[daemon] = p + if p.poll() and p.returncode: + self.logger.error( + '%s: Failed to launch "%s" (%s) with perf using: %s', + self, + daemon, + p.returncode, + cmd, + ) + else: + logger.debug( + "%s: %s %s started with perf", self, self.routertype, daemon + ) else: if daemon != "snmpd": cmdopt += " -d " @@ -1859,7 +1880,7 @@ class Router(Node): else "", ) else: - logger.info("%s: %s %s started", self, self.routertype, daemon) + logger.debug("%s: %s %s started", self, self.routertype, daemon) # Start mgmtd first if "mgmtd" in daemons_list: @@ -1888,15 +1909,6 @@ class Router(Node): while "snmpd" in daemons_list: daemons_list.remove("snmpd") - if daemons is None: - # Fix Link-Local Addresses on initial startup - # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this - _, output, _ = self.cmd_status( - "for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; echo $i: $mac; [ -z \"$mac\" ] && continue; IFS=':'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done", - stderr=subprocess.STDOUT, - ) - logger.debug("Set MACs:\n%s", output) - # Now start all the other daemons for daemon in daemons_list: if self.daemons[daemon] == 0: @@ -1904,16 +1916,50 @@ class Router(Node): start_daemon(daemon) # Check if daemons are running. - rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype) - if re.search(r"No such file or directory", rundaemons): - return "Daemons are not running" + wait_time = 30 if (gdb_routers or gdb_daemons) else 10 + timeout = Timeout(wait_time) + for remaining in timeout: + if not check_daemon_files: + break + check = check_daemon_files[0] + if self.path_exists(check): + check_daemon_files.pop(0) + continue + self.logger.debug("Waiting {}s for {} to appear".format(remaining, check)) + time.sleep(0.5) + + if check_daemon_files: + assert False, "Timeout({}) waiting for {} to appear on {}".format( + wait_time, check_daemon_files[0], self.name + ) # Update the permissions on the log files self.cmd("chown frr:frr -R {}/{}".format(self.logdir, self.name)) self.cmd("chmod ug+rwX,o+r -R {}/{}".format(self.logdir, self.name)) + if "frr" in logd_options: + logdopt = logd_options["frr"] + if "all" in logdopt or self.name in logdopt: + tail_log_files.append("{}/{}/frr.log".format(self.logdir, self.name)) + + for tailf in tail_log_files: + self.run_in_window("tail -f " + tailf, title=tailf, background=True) + return "" + def pid_exists(self, pid): + if pid <= 0: + return False + try: + # If we are not using PID namespaces then we will be a parent of the pid, + # otherwise the init process of the PID namespace will have reaped the proc. + os.waitpid(pid, os.WNOHANG) + except Exception: + pass + + rc, o, e = self.cmd_status("kill -0 " + str(pid), warn=False) + return rc == 0 or "No such process" not in e + def killRouterDaemons( self, daemons, wait=True, assertOnError=True, minErrorVersion="5.1" ): @@ -1933,15 +1979,15 @@ class Router(Node): if re.search(r"%s" % daemon, d): daemonpidfile = d.rstrip() daemonpid = self.cmd("cat %s" % daemonpidfile).rstrip() - if daemonpid.isdigit() and pid_exists(int(daemonpid)): - logger.info( + if daemonpid.isdigit() and self.pid_exists(int(daemonpid)): + logger.debug( "{}: killing {}".format( self.name, os.path.basename(daemonpidfile.rsplit(".", 1)[0]), ) ) - os.kill(int(daemonpid), signal.SIGKILL) - if pid_exists(int(daemonpid)): + self.cmd_status("kill -KILL {}".format(daemonpid)) + if self.pid_exists(int(daemonpid)): numRunning += 1 while wait and numRunning > 0: sleep( @@ -1955,7 +2001,7 @@ class Router(Node): for d in dmns[:-1]: if re.search(r"%s" % daemon, d): daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip() - if daemonpid.isdigit() and pid_exists( + if daemonpid.isdigit() and self.pid_exists( int(daemonpid) ): logger.info( @@ -1966,8 +2012,10 @@ class Router(Node): ), ) ) - os.kill(int(daemonpid), signal.SIGKILL) - if daemonpid.isdigit() and not pid_exists( + self.cmd_status( + "kill -KILL {}".format(daemonpid) + ) + if daemonpid.isdigit() and not self.pid_exists( int(daemonpid) ): numRunning -= 1 @@ -2198,7 +2246,7 @@ class Router(Node): log = self.getStdErr(daemon) if "memstats" in log: # Found memory leak - logger.info( + logger.warning( "\nRouter {} {} StdErr Log:\n{}".format(self.name, daemon, log) ) if not leakfound: diff --git a/tests/topotests/mgmt_tests/test_yang_mgmt.py b/tests/topotests/mgmt_tests/test_yang_mgmt.py index 06c18d7dfa..921b4e622c 100644 --- a/tests/topotests/mgmt_tests/test_yang_mgmt.py +++ b/tests/topotests/mgmt_tests/test_yang_mgmt.py @@ -217,7 +217,9 @@ def test_mgmt_commit_check(request): ] } } - result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) assert ( result is not True ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) @@ -319,7 +321,9 @@ def test_mgmt_commit_abort(request): ] } } - result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) assert ( result is not True ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) @@ -372,7 +376,7 @@ def test_mgmt_delete_config(request): assert ( result is True ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) - + step("Mgmt delete config") raw_config = { "r1": { @@ -387,7 +391,9 @@ def test_mgmt_delete_config(request): assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) step("Verify that the route is deleted from RIB") - result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) assert ( result is not True ), "Testcase {} : Failed" "Error: Routes is still present in RIB".format(tc_name) @@ -461,7 +467,9 @@ def test_mgmt_chaos_stop_start_frr(request): dut = "r1" protocol = "static" input_dict_4 = {"r1": {"static_routes": [{"network": "192.1.11.200/32"}]}} - result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) assert ( result is not True ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) diff --git a/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json b/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json new file mode 100644 index 0000000000..d7053bf6dd --- /dev/null +++ b/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json @@ -0,0 +1,249 @@ +{ + "address_types": ["ipv6"], + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r4": {"ipv6": "auto", "pim6": "enable"}, + "r2": {"ipv6": "auto", "pim6": "enable"}, + "r3": {"ipv6": "auto", "pim6": "enable"}, + "i1": {"ipv6": "auto", "pim6": "enable"}, + "i2": {"ipv6": "auto", "pim6": "enable"} + }, + + "bgp": { + "local_as": "100", + "router_id": "192.168.1.1", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1": {} + } + }, + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r4": { + "dest_link": { + "r1": {} + } + }, + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"}, + "r1": {"ipv6": "auto", "pim6": "enable"}, + "r4": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "100", + "router_id": "192.168.1.2", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "r1": {"ipv6": "auto", "pim6": "enable"}, + "r4": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "100", + "router_id": "192.168.1.3", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "r2": {"ipv6": "auto", "pim6": "enable"}, + "r3": {"ipv6": "auto", "pim6": "enable"}, + "i4": {"ipv6": "auto", "pim6": "enable"}, + "r1": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "100", + "router_id": "192.168.1.4", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4": {} + } + }, + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r4": {} + } + }, + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "i1": { + "links": { + "r1": {"ipv6": "auto"} + } + }, + "i2": { + "links": { + "r1": {"ipv6": "auto"} + } + }, + "i4": { + "links": { + "r4": {"ipv6": "auto"} + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py b/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py new file mode 100644 index 0000000000..2c4fb4e998 --- /dev/null +++ b/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py @@ -0,0 +1,915 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2023 by VMware, Inc. ("VMware") +# + +""" +Following tests are covered to test_multicast_pim_mld_local_tier_1: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: + +1. Verify static MLD group populated when static "ip mld join <grp>" in configured +2. Verify mroute and upstream populated with correct OIL/IIF with static imld join +3. Verify local MLD join not allowed for non multicast group +4. Verify static MLD group removed from DUT while removing "ip mld join" CLI +5. Verify static MLD groups after removing and adding MLD config +""" + +import os +import sys +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 re import search as re_search +from re import findall as findall + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + kill_router_daemons, + start_router_daemons, + reset_config_on_routers, + do_countdown, + apply_raw_config, + socat_send_pim6_traffic, +) + +from lib.pim import ( + create_pim_config, + verify_mroutes, + verify_upstream_iif, + verify_mld_groups, + clear_pim6_mroute, + McastTesterHelper, + verify_pim_neighbors, + create_mld_config, + verify_mld_groups, + verify_local_mld_groups, + verify_pim_rp_info, +) +from lib.topolog import logger +from lib.topojson import build_config_from_json + +r1_r2_links = [] +r1_r3_links = [] +r2_r1_links = [] +r2_r4_links = [] +r3_r1_links = [] +r3_r4_links = [] +r4_r2_links = [] +r4_r3_links = [] + +pytestmark = [pytest.mark.pim6d, pytest.mark.staticd] + +TOPOLOGY = """ + +-------------------+ + | | + i1--- R1-------R2----------R4---i2 + | | + +-------R3----------+ + + + Description: + i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send MLD + join and traffic + R1 - DUT (LHR) + R2 - RP + R3 - Transit + R4 - (FHR) + +""" +# Global variables + +GROUP_RANGE = "ffaa::/16" +RP_RANGE = "ff00::/8" +GROUP_RANGE_1 = [ + "ffaa::1/128", + "ffaa::2/128", + "ffaa::3/128", + "ffaa::4/128", + "ffaa::5/128", +] +MLD_JOIN_RANGE_1 = ["ffaa::1", "ffaa::2", "ffaa::3", "ffaa::4", "ffaa::5"] +MLD_JOIN_RANGE_2 = [ + "ff02::1:ff00:0", + "ff02::d", + "fe80::250:56ff:feb7:d8d5", + "2001::4", + "2002::5", +] + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + 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... + json_file = "{}/multicast_mld_local_join.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # 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, " Verify PIM neighbor: Failed 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 test_mld_local_joins_p0(request): + """ + Verify static MLD group populated when static + "ipv6 mld join <grp>" in configured + """ + + 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) + + reset_config_on_routers(tgen) + + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"] + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + intf_r1_i2: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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("verify static mld join using show ipv6 mld join") + dut = "r1" + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify mld groups using show ipv6 mld groups") + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_mroute_with_mld_local_joins_p0(request): + """ + Verify mroute and upstream populated with correct OIL/IIF with + static mld join + """ + 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) + + reset_config_on_routers(tgen) + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"] + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + intf_r1_i2: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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("verify static mld join using show ipv6 mld join") + dut = "r1" + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify mld groups using show ipv6 mld groups") + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify RP-info populated in DUT") + dut = "r1" + rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + SOURCE = "Static" + oif = topo["routers"]["r1"]["links"]["r2"]["interface"] + result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)") + intf_ip = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + intf = topo["routers"]["i4"]["links"]["r4"]["interface"] + result = socat_send_pim6_traffic(tgen, "i4", "UDP6-SEND", MLD_JOIN_RANGE_1, intf) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + + intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"] + + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_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"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Verify mroutes not created with local interface ip ") + input_dict_local_sg = [ + { + "dut": "r1", + "src_address": intf_r1_i1, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": intf_r1_i2, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + for data in input_dict_local_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed Error: {}" + "sg created with local interface ip".format(tc_name, result) + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + MLD_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed Error: {}" + "upstream created with local interface ip".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_remove_add_mld_local_joins_p1(request): + """ + Verify static MLD group removed from DUT while + removing "ip mld join" CLI + """ + + 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) + + reset_config_on_routers(tgen) + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}} + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify RP-info populated in DUT") + dut = "r1" + rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + SOURCE = "Static" + oif = topo["routers"]["r1"]["links"]["r2"]["interface"] + result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)") + intf_ip = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + intf = topo["routers"]["i4"]["links"]["r4"]["interface"] + result = socat_send_pim6_traffic(tgen, "i4", "UDP6-SEND", MLD_JOIN_RANGE_1, intf) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + + intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"] + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_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"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Remove MLD join from DUT") + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: { + "mld": { + "join": MLD_JOIN_RANGE_1, + "delete_attr": True, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join removed using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups( + tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False + ) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "MLD join still present".format( + tc_name, result + ) + + step("verify mld groups removed using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "MLD groups still present".format( + tc_name, result + ) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed Error: {}" "mroutes still present".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + MLD_JOIN_RANGE_1, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed Error: {}" "mroutes still present".format( + tc_name, result + ) + + step("Add MLD join on DUT again") + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: { + "mld": { + "join": MLD_JOIN_RANGE_1, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_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"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_remove_add_mld_config_with_local_joins_p1(request): + """ + Verify static MLD groups after removing + and adding MLD config + """ + + 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) + + reset_config_on_routers(tgen) + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}} + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)") + intf_ip = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + intf = topo["routers"]["i4"]["links"]["r4"]["interface"] + result = socat_send_pim6_traffic(tgen, "i4", "UDP6-SEND", MLD_JOIN_RANGE_1, intf) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + + intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"] + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_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"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Remove mld and mld version 2 from DUT interface") + input_dict = { + "r1": { + "mld": { + "interfaces": {intf_r1_i1: {"mld": {"version": "1", "delete": True}}} + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups( + tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify mld groups using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "MLD grsp still present".format( + tc_name, result + ) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed Error: {}" "mroutes still present".format( + tc_name, result + ) + + step("Add mld and mld version 2 from DUT interface") + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}} + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_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"], MLD_JOIN_RANGE_1 + ) + 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_pim6_sm_topo1/test_multicast_pim6_sm1.py b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py index a76ff2dd9c..87b04b41be 100644 --- a/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py +++ b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py @@ -62,6 +62,7 @@ from lib.common_config import ( socat_send_mld_join, socat_send_pim6_traffic, get_frr_ipv6_linklocal, + kill_socat, ) from lib.bgp import create_router_bgp from lib.pim import ( @@ -158,10 +159,6 @@ def setup_module(mod): # Creating configuration from JSON build_config_from_json(tgen, tgen.json_topo) - # XXX Replace this using "with McastTesterHelper()... " in each test if possible. - global app_helper - app_helper = McastTesterHelper(tgen) - logger.info("Running setup_module() done") @@ -172,7 +169,8 @@ def teardown_module(): tgen = get_topogen() - app_helper.cleanup() + # Clean up socat + kill_socat(tgen) # Stop toplogy and Remove tmp files tgen.stop_topology() diff --git a/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py index ceef68fece..788a839918 100644 --- a/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py +++ b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py @@ -53,6 +53,7 @@ from lib.common_config import ( socat_send_mld_join, socat_send_pim6_traffic, get_frr_ipv6_linklocal, + kill_socat, ) from lib.bgp import create_router_bgp from lib.pim import ( @@ -149,10 +150,6 @@ def setup_module(mod): # Creating configuration from JSON build_config_from_json(tgen, tgen.json_topo) - # XXX Replace this using "with McastTesterHelper()... " in each test if possible. - global app_helper - app_helper = McastTesterHelper(tgen) - logger.info("Running setup_module() done") @@ -163,7 +160,8 @@ def teardown_module(): tgen = get_topogen() - app_helper.cleanup() + # Clean up socat + kill_socat(tgen) # Stop toplogy and Remove tmp files tgen.stop_topology() diff --git a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py index 95b4004e14..977cd477c8 100755 --- a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py +++ b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py @@ -172,6 +172,9 @@ def teardown_module(): logger.info("Running teardown_module to delete topology") tgen = get_topogen() + # Clean up socat + kill_socat(tgen) + # Stop toplogy and Remove tmp files tgen.stop_topology() diff --git a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py index 2fedb6e517..a61164baa2 100755 --- a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py +++ b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py @@ -176,6 +176,9 @@ def teardown_module(): logger.info("Running teardown_module to delete topology") tgen = get_topogen() + # Clean up socat + kill_socat(tgen) + # Stop toplogy and Remove tmp files tgen.stop_topology() diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json new file mode 100644 index 0000000000..715aa1de72 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json @@ -0,0 +1 @@ +{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json new file mode 100644 index 0000000000..3bbcce1370 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json @@ -0,0 +1,51 @@ +{ + "totalGroups":5, + "watermarkLimit":0, + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "state":"up", + "address":"10.0.8.2", + "index":"*", + "flagMulticast":true, + "flagBroadcast":true, + "lanDelayEnabled":true, + "groups":[ + { + "group":"225.1.1.1", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.2", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.3", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.4", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.5", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + } + ] + } +} + diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json new file mode 100644 index 0000000000..876befa1b8 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json @@ -0,0 +1 @@ +{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json new file mode 100644 index 0000000000..a3fb496d25 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json @@ -0,0 +1,22 @@ +{ + "totalGroups":5, + "watermarkLimit":0, + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "state":"up", + "address":"10.0.8.2", + "index":"*", + "flagMulticast":true, + "flagBroadcast":true, + "lanDelayEnabled":true, + "groups":[ + { + "group":"225.1.1.5", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + } + ] + } +} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json new file mode 100644 index 0000000000..11ac5a01e7 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json @@ -0,0 +1 @@ +{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json new file mode 100644 index 0000000000..10ae1afc90 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json @@ -0,0 +1,61 @@ +{ + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "225.1.1.1":{ + "group":"225.1.1.1", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.2":{ + "group":"225.1.1.2", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.3":{ + "group":"225.1.1.3", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.4":{ + "group":"225.1.1.4", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.5":{ + "group":"225.1.1.5", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + } + } +} + diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json new file mode 100644 index 0000000000..7a19975bee --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json @@ -0,0 +1,16 @@ +{ + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "225.1.1.4":{ + "group":"225.1.1.4", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + } + } +} 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 index 2ffd3a3ac0..2c1241c0cc 100755 --- 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 @@ -42,6 +42,8 @@ import time import datetime import pytest from time import sleep +import json +import functools pytestmark = pytest.mark.pimd @@ -54,8 +56,8 @@ sys.path.append(os.path.join(CWD, "../lib/")) # pylint: disable=C0413 # Import topogen and topotest helpers -from lib.topogen import Topogen, get_topogen - +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen from lib.common_config import ( start_topology, write_test_header, @@ -1510,6 +1512,108 @@ def test_verify_remove_add_igmp_config_to_receiver_interface_p0(request): ) assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + # IGMP JSON verification + step("Verify IGMP group and source JSON for single interface and group") + router = tgen.gears["l1"] + + reffile = os.path.join(CWD, "igmp_group_all_detail.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups detail json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group detailed output on l1 for all interfaces and all groups is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_group_all_brief.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group output on l1 for all groups in interface l1-i1-eth1 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_group_all_detail.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 detail json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group detailed output on l1 for all groups in interface l1-i1-eth1 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_single_group_brief.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 225.1.1.5 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group output on l1 for interface l1-i1-eth1 and group 225.1.1.5 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_single_group_detail.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 225.1.1.5 detail json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group detailed output on l1 for interface l1-i1-eth1 and group 225.1.1.5 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_source_single_if_group_all.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp sources l1-i1-eth1 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP source output on l1 for interface l1-i1-eth1 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_source_single_if_single_group.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp sources l1-i1-eth1 225.1.1.4 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP source output on l1 for interface l1-i1-eth1 and group 225.1.1.4 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + step( "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from" " receiver interface of FRR1" diff --git a/tests/topotests/munet/__init__.py b/tests/topotests/munet/__init__.py new file mode 100644 index 0000000000..e1f18e51e6 --- /dev/null +++ b/tests/topotests/munet/__init__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 30 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module to import various objects to root namespace.""" +from .base import BaseMunet +from .base import Bridge +from .base import Commander +from .base import LinuxNamespace +from .base import SharedNamespace +from .base import cmd_error +from .base import comm_error +from .base import get_exec_path +from .base import proc_error +from .native import L3Bridge +from .native import L3NamespaceNode +from .native import Munet +from .native import to_thread + + +__all__ = [ + "BaseMunet", + "Bridge", + "Commander", + "L3Bridge", + "L3NamespaceNode", + "LinuxNamespace", + "Munet", + "SharedNamespace", + "cmd_error", + "comm_error", + "get_exec_path", + "proc_error", + "to_thread", +] diff --git a/tests/topotests/munet/__main__.py b/tests/topotests/munet/__main__.py new file mode 100644 index 0000000000..4419ab94a2 --- /dev/null +++ b/tests/topotests/munet/__main__.py @@ -0,0 +1,236 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 2 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""The main function for standalone operation.""" +import argparse +import asyncio +import logging +import logging.config +import os +import subprocess +import sys + +from . import cli +from . import parser +from .base import get_event_loop +from .cleanup import cleanup_previous +from .compat import PytestConfig + + +logger = None + + +async def forever(): + while True: + await asyncio.sleep(3600) + + +async def run_and_wait(args, unet): + tasks = [] + + if not args.topology_only: + # add the cmd.wait()s returned from unet.run() + tasks += await unet.run() + + if sys.stdin.isatty() and not args.no_cli: + # Run an interactive CLI + task = cli.async_cli(unet) + else: + if args.no_wait: + logger.info("Waiting for all node cmd to complete") + task = asyncio.gather(*tasks, return_exceptions=True) + else: + logger.info("Waiting on signal to exit") + task = asyncio.create_task(forever()) + task = asyncio.gather(task, *tasks, return_exceptions=True) + + try: + await task + finally: + # Basically we are canceling tasks from unet.run() which are just async calls to + # node.cmd_p.wait() so we've stopped waiting for them to complete, but not + # actually canceld/killed the cmd_p process. + for task in tasks: + task.cancel() + + +async def async_main(args, config): + status = 3 + + # Setup the namespaces and network addressing. + + unet = await parser.async_build_topology( + config, rundir=args.rundir, args=args, pytestconfig=PytestConfig(args) + ) + logger.info("Topology up: rundir: %s", unet.rundir) + + try: + status = await run_and_wait(args, unet) + except KeyboardInterrupt: + logger.info("Exiting, received KeyboardInterrupt in async_main") + except asyncio.CancelledError as ex: + logger.info("task canceled error: %s cleaning up", ex) + except Exception as error: + logger.info("Exiting, unexpected exception %s", error, exc_info=True) + else: + logger.info("Exiting normally") + + logger.debug("main: async deleting") + try: + await unet.async_delete() + except KeyboardInterrupt: + status = 2 + logger.warning("Received KeyboardInterrupt while cleaning up.") + except Exception as error: + status = 2 + logger.info("Deleting, unexpected exception %s", error, exc_info=True) + return status + + +def main(*args): + ap = argparse.ArgumentParser(args) + cap = ap.add_argument_group(title="Config", description="config related options") + + cap.add_argument("-c", "--config", help="config file (yaml, toml, json, ...)") + cap.add_argument( + "-d", "--rundir", help="runtime directory for tempfiles, logs, etc" + ) + cap.add_argument( + "--kinds-config", + help="kinds config file, overrides default search (yaml, toml, json, ...)", + ) + cap.add_argument( + "--project-root", help="directory to stop searching for kinds config at" + ) + rap = ap.add_argument_group(title="Runtime", description="runtime related options") + rap.add_argument( + "-C", + "--cleanup", + action="store_true", + help="Remove the entire rundir (not just node subdirs) prior to running.", + ) + rap.add_argument( + "--gdb", metavar="NODE-LIST", help="comma-sep list of hosts to run gdb on" + ) + rap.add_argument( + "--gdb-breakpoints", + metavar="BREAKPOINT-LIST", + help="comma-sep list of breakpoints to set", + ) + rap.add_argument( + "--host", + action="store_true", + help="no isolation for top namespace, bridges exposed to default namespace", + ) + rap.add_argument( + "--pcap", + metavar="TARGET-LIST", + help="comma-sep list of capture targets (NETWORK or NODE:IFNAME)", + ) + rap.add_argument( + "--shell", metavar="NODE-LIST", help="comma-sep list of nodes to open shells on" + ) + rap.add_argument( + "--stderr", + metavar="NODE-LIST", + help="comma-sep list of nodes to open windows viewing stderr", + ) + rap.add_argument( + "--stdout", + metavar="NODE-LIST", + help="comma-sep list of nodes to open windows viewing stdout", + ) + rap.add_argument( + "--topology-only", + action="store_true", + help="Do not run any node commands", + ) + rap.add_argument("--unshare-inline", action="store_true", help=argparse.SUPPRESS) + rap.add_argument( + "--validate-only", + action="store_true", + help="Validate the config against the schema definition", + ) + rap.add_argument("-v", "--verbose", action="store_true", help="be verbose") + rap.add_argument( + "-V", "--version", action="store_true", help="print the verison number and exit" + ) + eap = ap.add_argument_group(title="Uncommon", description="uncommonly used options") + eap.add_argument("--log-config", help="logging config file (yaml, toml, json, ...)") + eap.add_argument( + "--no-kill", + action="store_true", + help="Do not kill previous running processes", + ) + eap.add_argument( + "--no-cli", action="store_true", help="Do not run the interactive CLI" + ) + eap.add_argument("--no-wait", action="store_true", help="Exit after commands") + + args = ap.parse_args() + + if args.version: + from importlib import metadata # pylint: disable=C0415 + + print(metadata.version("munet")) + sys.exit(0) + + rundir = args.rundir if args.rundir else "/tmp/munet" + args.rundir = rundir + + if args.cleanup: + if os.path.exists(rundir): + if not os.path.exists(f"{rundir}/config.json"): + logging.critical( + 'unsafe: won\'t clean up rundir "%s" as ' + "previous config.json not present", + rundir, + ) + sys.exit(1) + else: + subprocess.run(["/usr/bin/rm", "-rf", rundir], check=True) + + subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True) + os.environ["MUNET_RUNDIR"] = rundir + + parser.setup_logging(args) + + global logger # pylint: disable=W0603 + logger = logging.getLogger("munet") + + config = parser.get_config(args.config) + logger.info("Loaded config from %s", config["config_pathname"]) + if not config["topology"]["nodes"]: + logger.critical("No nodes defined in config file") + return 1 + + if not args.no_kill: + cleanup_previous() + + loop = None + status = 4 + try: + parser.validate_config(config, logger, args) + if args.validate_only: + return 0 + # Executes the cmd for each node. + loop = get_event_loop() + status = loop.run_until_complete(async_main(args, config)) + except KeyboardInterrupt: + logger.info("Exiting, received KeyboardInterrupt in main") + except Exception as error: + logger.info("Exiting, unexpected exception %s", error, exc_info=True) + finally: + if loop: + loop.close() + + return status + + +if __name__ == "__main__": + exit_status = main() + sys.exit(exit_status) diff --git a/tests/topotests/munet/base.py b/tests/topotests/munet/base.py new file mode 100644 index 0000000000..eb4b088442 --- /dev/null +++ b/tests/topotests/munet/base.py @@ -0,0 +1,3068 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# July 9 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module that implements core functionality for library or standalone use.""" +import asyncio +import datetime +import errno +import ipaddress +import logging +import os +import platform +import re +import readline +import shlex +import signal +import subprocess +import sys +import tempfile +import time as time_mod + +from collections import defaultdict +from pathlib import Path +from typing import Union + +from . import config as munet_config +from . import linux + + +try: + import pexpect + + from pexpect.fdpexpect import fdspawn + from pexpect.popen_spawn import PopenSpawn + + have_pexpect = True +except ImportError: + have_pexpect = False + +PEXPECT_PROMPT = "PEXPECT_PROMPT>" +PEXPECT_CONTINUATION_PROMPT = "PEXPECT_PROMPT+" + +root_hostname = subprocess.check_output("hostname") +our_pid = os.getpid() + + +class MunetError(Exception): + """A generic munet error.""" + + +class CalledProcessError(subprocess.CalledProcessError): + """Improved logging subclass of subprocess.CalledProcessError.""" + + def __str__(self): + o = self.output.strip() if self.output else "" + e = self.stderr.strip() if self.stderr else "" + s = f"returncode: {self.returncode} command: {self.cmd}" + o = "\n\tstdout: " + o if o else "" + e = "\n\tstderr: " + e if e else "" + return s + o + e + + def __repr__(self): + o = self.output.strip() if self.output else "" + e = self.stderr.strip() if self.stderr else "" + return f"munet.base.CalledProcessError({self.returncode}, {self.cmd}, {o}, {e})" + + +class Timeout: + """An object to passively monitor for timeouts.""" + + def __init__(self, delta): + self.delta = datetime.timedelta(seconds=delta) + self.started_on = datetime.datetime.now() + self.expires_on = self.started_on + self.delta + + def elapsed(self): + elapsed = datetime.datetime.now() - self.started_on + return elapsed.total_seconds() + + def is_expired(self): + return datetime.datetime.now() > self.expires_on + + def remaining(self): + remaining = self.expires_on - datetime.datetime.now() + return remaining.total_seconds() + + def __iter__(self): + return self + + def __next__(self): + remaining = self.remaining() + if remaining <= 0: + raise StopIteration() + return remaining + + +def fsafe_name(name): + return "".join(x if x.isalnum() else "_" for x in name) + + +def indent(s): + return "\t" + s.replace("\n", "\n\t") + + +def shell_quote(command): + """Return command wrapped in single quotes.""" + if sys.version_info[0] >= 3: + return shlex.quote(command) + return "'" + command.replace("'", "'\"'\"'") + "'" + + +def cmd_error(rc, o, e): + s = f"rc {rc}" + o = "\n\tstdout: " + o.strip() if o and o.strip() else "" + e = "\n\tstderr: " + e.strip() if e and e.strip() else "" + return s + o + e + + +def proc_str(p): + if hasattr(p, "args"): + args = p.args if isinstance(p.args, str) else " ".join(p.args) + else: + args = "" + return f"proc pid: {p.pid} args: {args}" + + +def proc_error(p, o, e): + if hasattr(p, "args"): + args = p.args if isinstance(p.args, str) else " ".join(p.args) + else: + args = "" + + s = f"rc {p.returncode} pid {p.pid}" + a = "\n\targs: " + args if args else "" + o = "\n\tstdout: " + (o.strip() if o and o.strip() else "*empty*") + e = "\n\tstderr: " + (e.strip() if e and e.strip() else "*empty*") + return s + a + o + e + + +def comm_error(p): + rc = p.poll() + assert rc is not None + if not hasattr(p, "saved_output"): + p.saved_output = p.communicate() + return proc_error(p, *p.saved_output) + + +async def acomm_error(p): + rc = p.returncode + assert rc is not None + if not hasattr(p, "saved_output"): + p.saved_output = await p.communicate() + return proc_error(p, *p.saved_output) + + +def get_kernel_version(): + kvs = ( + subprocess.check_output("uname -r", shell=True, text=True).strip().split("-", 1) + ) + kv = kvs[0].split(".") + kv = [int(x) for x in kv] + return kv + + +def convert_number(value) -> int: + """Convert a number value with a possible suffix to an integer. + + >>> convert_number("100k") == 100 * 1024 + True + >>> convert_number("100M") == 100 * 1000 * 1000 + True + >>> convert_number("100Gi") == 100 * 1024 * 1024 * 1024 + True + >>> convert_number("55") == 55 + True + """ + if value is None: + raise ValueError("Invalid value None for convert_number") + rate = str(value) + base = 1000 + if rate[-1] == "i": + base = 1024 + rate = rate[:-1] + suffix = "KMGTPEZY" + index = suffix.find(rate[-1]) + if index == -1: + base = 1024 + index = suffix.lower().find(rate[-1]) + if index != -1: + rate = rate[:-1] + return int(rate) * base ** (index + 1) + + +def is_file_like(fo): + return isinstance(fo, int) or hasattr(fo, "fileno") + + +def get_tc_bits_value(user_value): + value = convert_number(user_value) / 1000 + return f"{value:03f}kbit" + + +def get_tc_bytes_value(user_value): + # Raw numbers are bytes in tc + return convert_number(user_value) + + +def get_tmp_dir(uniq): + return os.path.join(tempfile.mkdtemp(), uniq) + + +async def _async_get_exec_path(binary, cmdf, cache): + if isinstance(binary, str): + bins = [binary] + else: + bins = binary + for b in bins: + if b in cache: + return cache[b] + + rc, output, _ = await cmdf("which " + b, warn=False) + if not rc: + cache[b] = os.path.abspath(output.strip()) + return cache[b] + return None + + +def _get_exec_path(binary, cmdf, cache): + if isinstance(binary, str): + bins = [binary] + else: + bins = binary + for b in bins: + if b in cache: + return cache[b] + + rc, output, _ = cmdf("which " + b, warn=False) + if not rc: + cache[b] = os.path.abspath(output.strip()) + return cache[b] + return None + + +def get_event_loop(): + """Configure and return our non-thread using event loop. + + This function configures a new child watcher to not use threads. + Threads cannot be used when we inline unshare a PID namespace. + """ + policy = asyncio.get_event_loop_policy() + loop = policy.get_event_loop() + owatcher = policy.get_child_watcher() + logging.debug( + "event_loop_fixture: global policy %s, current loop %s, current watcher %s", + policy, + loop, + owatcher, + ) + + policy.set_child_watcher(None) + owatcher.close() + + try: + watcher = asyncio.PidfdChildWatcher() # pylint: disable=no-member + except Exception: + watcher = asyncio.SafeChildWatcher() + loop = policy.get_event_loop() + + logging.debug( + "event_loop_fixture: attaching new watcher %s to loop and setting in policy", + watcher, + ) + watcher.attach_loop(loop) + policy.set_child_watcher(watcher) + policy.set_event_loop(loop) + assert asyncio.get_event_loop_policy().get_child_watcher() is watcher + + return loop + + +class Commander: # pylint: disable=R0904 + """An object that can execute commands.""" + + tmux_wait_gen = 0 + + def __init__(self, name, logger=None, unet=None, **kwargs): + """Create a Commander. + + Args: + name: name of the commander object + logger: logger to use for logging commands a defualt is supplied if this + is None + unet: unet that owns this object, only used by Commander in run_in_window, + otherwise can be None. + """ + # del kwargs # deal with lint warning + # logging.warning("Commander: name %s kwargs %s", name, kwargs) + + self.name = name + self.unet = unet + self.deleting = False + self.last = None + self.exec_paths = {} + + if not logger: + logname = f"munet.{self.__class__.__name__.lower()}.{name}" + self.logger = logging.getLogger(logname) + self.logger.setLevel(logging.DEBUG) + else: + self.logger = logger + + super().__init__(**kwargs) + + @property + def is_vm(self): + return False + + @property + def is_container(self): + return False + + def set_logger(self, logfile): + self.logger = logging.getLogger(__name__ + ".commander." + self.name) + self.logger.setLevel(logging.DEBUG) + if isinstance(logfile, str): + handler = logging.FileHandler(logfile, mode="w") + else: + handler = logging.StreamHandler(logfile) + + fmtstr = "%(asctime)s.%(msecs)03d %(levelname)s: {}({}): %(message)s".format( + self.__class__.__name__, self.name + ) + handler.setFormatter(logging.Formatter(fmt=fmtstr)) + self.logger.addHandler(handler) + + def _get_pre_cmd(self, use_str, use_pty, **kwargs): + """Get the pre-user-command values. + + The values returned here should be what is required to cause the user's command + to execute in the correct context (e.g., namespace, container, sshremote). + """ + del kwargs + del use_pty + return "" if use_str else [] + + def __str__(self): + return f"{self.__class__.__name__}({self.name})" + + async def async_get_exec_path(self, binary): + """Return the full path to the binary executable. + + `binary` :: binary name or list of binary names + """ + return await _async_get_exec_path( + binary, self.async_cmd_status_nsonly, self.exec_paths + ) + + def get_exec_path(self, binary): + """Return the full path to the binary executable. + + `binary` :: binary name or list of binary names + """ + return _get_exec_path(binary, self.cmd_status_nsonly, self.exec_paths) + + def get_exec_path_host(self, binary): + """Return the full path to the binary executable. + + If the object is actually a derived class (e.g., a container) this method will + return the exec path for the native namespace rather than the container. The + path is the one which the other xxx_host methods will use. + + `binary` :: binary name or list of binary names + """ + return get_exec_path_host(binary) + + def test(self, flags, arg): + """Run test binary, with flags and arg.""" + test_path = self.get_exec_path(["test"]) + rc, _, _ = self.cmd_status([test_path, flags, arg], warn=False) + return not rc + + def test_nsonly(self, flags, arg): + """Run test binary, with flags and arg.""" + test_path = self.get_exec_path(["test"]) + rc, _, _ = self.cmd_status_nsonly([test_path, flags, arg], warn=False) + return not rc + + def path_exists(self, path): + """Check if path exists.""" + return self.test("-e", path) + + async def cleanup_pid(self, pid, kill_pid=None): + """Signal a pid to exit with escalating forcefulness.""" + if kill_pid is None: + kill_pid = pid + + for sn in (signal.SIGHUP, signal.SIGKILL): + self.logger.debug( + "%s: %s %s (wait %s)", self, signal.Signals(sn).name, kill_pid, pid + ) + + os.kill(kill_pid, sn) + + # No need to wait after this. + if sn == signal.SIGKILL: + return + + # try each signal, waiting 15 seconds for exit before advancing + wait_sec = 30 + self.logger.debug("%s: waiting %ss for pid to exit", self, wait_sec) + for _ in Timeout(wait_sec): + try: + status = os.waitpid(pid, os.WNOHANG) + if status == (0, 0): + await asyncio.sleep(0.1) + else: + self.logger.debug("pid %s exited status %s", pid, status) + return + except OSError as error: + if error.errno == errno.ECHILD: + self.logger.debug("%s: pid %s was reaped", self, pid) + else: + self.logger.warning( + "%s: error waiting on pid %s: %s", self, pid, error + ) + return + self.logger.debug("%s: timeout waiting on pid %s to exit", self, pid) + + def _get_sub_args(self, cmd_list, defaults, use_pty=False, ns_only=False, **kwargs): + """Returns pre-command, cmd, and default keyword args.""" + assert not isinstance(cmd_list, str) + + defaults["shell"] = False + pre_cmd_list = self._get_pre_cmd(False, use_pty, ns_only=ns_only, **kwargs) + cmd_list = [str(x) for x in cmd_list] + + # os_env = {k: v for k, v in os.environ.items() if k.startswith("MUNET")} + # env = {**os_env, **(kwargs["env"] if "env" in kwargs else {})} + env = {**(kwargs["env"] if "env" in kwargs else os.environ)} + if "MUNET_NODENAME" not in env: + env["MUNET_NODENAME"] = self.name + kwargs["env"] = env + + defaults.update(kwargs) + + return pre_cmd_list, cmd_list, defaults + + def _common_prologue(self, async_exec, method, cmd, skip_pre_cmd=False, **kwargs): + cmd_list = self._get_cmd_as_list(cmd) + if method == "_spawn": + defaults = { + "encoding": "utf-8", + "codec_errors": "ignore", + } + else: + defaults = { + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE, + } + if not async_exec: + defaults["encoding"] = "utf-8" + + pre_cmd_list, cmd_list, defaults = self._get_sub_args( + cmd_list, defaults, **kwargs + ) + + use_pty = kwargs.get("use_pty", False) + if method == "_spawn": + # spawn doesn't take "shell" keyword arg + if "shell" in defaults: + del defaults["shell"] + # this is required to avoid receiving a STOPPED signal on expect! + if not use_pty: + defaults["preexec_fn"] = os.setsid + defaults["env"]["PS1"] = "$ " + + self.logger.debug( + '%s: %s %s("%s", pre_cmd: "%s" use_pty: %s kwargs: %.120s)', + self, + "XXX" if method == "_spawn" else "", + method, + cmd_list, + pre_cmd_list if not skip_pre_cmd else "", + use_pty, + defaults, + ) + + actual_cmd_list = cmd_list if skip_pre_cmd else pre_cmd_list + cmd_list + return actual_cmd_list, defaults + + async def _async_popen(self, method, cmd, **kwargs): + """Create a new asynchronous subprocess.""" + acmd, kwargs = self._common_prologue(True, method, cmd, **kwargs) + p = await asyncio.create_subprocess_exec(*acmd, **kwargs) + return p, acmd + + def _popen(self, method, cmd, **kwargs): + """Create a subprocess.""" + acmd, kwargs = self._common_prologue(False, method, cmd, **kwargs) + p = subprocess.Popen(acmd, **kwargs) + return p, acmd + + def _fdspawn(self, fo, **kwargs): + defaults = {} + defaults.update(kwargs) + + if "echo" in defaults: + del defaults["echo"] + + if "encoding" not in defaults: + defaults["encoding"] = "utf-8" + if "codec_errors" not in defaults: + defaults["codec_errors"] = "ignore" + encoding = defaults["encoding"] + + self.logger.debug("%s: _fdspawn(%s, kwargs: %s)", self, fo, defaults) + + p = fdspawn(fo, **defaults) + + # We don't have TTY like conversions of LF to CRLF + p.crlf = os.linesep.encode(encoding) + + # we own the socket now detach the file descriptor to keep it from closing + if hasattr(fo, "detach"): + fo.detach() + + return p + + def _spawn(self, cmd, skip_pre_cmd=False, use_pty=False, echo=False, **kwargs): + logging.debug( + '%s: XXX _spawn: cmd "%s" skip_pre_cmd %s use_pty %s echo %s kwargs %s', + self, + cmd, + skip_pre_cmd, + use_pty, + echo, + kwargs, + ) + actual_cmd, defaults = self._common_prologue( + False, "_spawn", cmd, skip_pre_cmd=skip_pre_cmd, use_pty=use_pty, **kwargs + ) + + self.logger.debug( + '%s: XXX %s("%s", use_pty %s echo %s defaults: %s)', + self, + "PopenSpawn" if not use_pty else "pexpect.spawn", + actual_cmd, + use_pty, + echo, + defaults, + ) + + # We don't specify a timeout it defaults to 30s is that OK? + if not use_pty: + p = PopenSpawn(actual_cmd, **defaults) + else: + p = pexpect.spawn(actual_cmd[0], actual_cmd[1:], echo=echo, **defaults) + return p, actual_cmd + + def spawn( + self, + cmd, + spawned_re, + expects=(), + sends=(), + use_pty=False, + logfile=None, + logfile_read=None, + logfile_send=None, + trace=None, + **kwargs, + ): + """Create a spawned send/expect process. + + Args: + cmd: list of args to exec/popen with, or an already open socket + spawned_re: what to look for to know when done, `spawn` returns when seen + expects: a list of regex other than `spawned_re` to look for. Commonly, + "ogin:" or "[Pp]assword:"r. + sends: what to send when an element of `expects` matches. So e.g., the + username or password if thats what corresponding expect matched. Can + be the empty string to send nothing. + use_pty: true for pty based expect, otherwise uses popen (pipes/files) + trace: if true then log send/expects + **kwargs - kwargs passed on the _spawn. + + Returns: + A pexpect process. + + Raises: + pexpect.TIMEOUT, pexpect.EOF as documented in `pexpect` + CalledProcessError if EOF is seen and `cmd` exited then + raises a CalledProcessError to indicate the failure. + """ + if is_file_like(cmd): + assert not use_pty + ac = "*socket*" + p = self._fdspawn(cmd, **kwargs) + else: + p, ac = self._spawn(cmd, use_pty=use_pty, **kwargs) + + if logfile: + p.logfile = logfile + if logfile_read: + p.logfile_read = logfile_read + if logfile_send: + p.logfile_send = logfile_send + + # for spawned shells (i.e., a direct command an not a console) + # this is wrong and will cause 2 prompts + if not use_pty: + # This isn't very nice looking + p.echo = False + if not is_file_like(cmd): + p.isalive = lambda: p.proc.poll() is None + if not hasattr(p, "close"): + p.close = p.wait + + # Do a quick check to see if we got the prompt right away, otherwise we may be + # at a console so we send a \n to re-issue the prompt + index = p.expect([spawned_re, pexpect.TIMEOUT, pexpect.EOF], timeout=0.1) + if index == 0: + assert p.match is not None + self.logger.debug( + "%s: got spawned_re quick: '%s' matching '%s'", + self, + p.match.group(0), + spawned_re, + ) + return p + + # Now send a CRLF to cause the prompt (or whatever else) to re-issue + p.send("\n") + try: + patterns = [spawned_re, *expects] + + self.logger.debug("%s: expecting: %s", self, patterns) + + while index := p.expect(patterns): + if trace: + assert p.match is not None + self.logger.debug( + "%s: got expect: '%s' matching %d '%s', sending '%s'", + self, + p.match.group(0), + index, + patterns[index], + sends[index - 1], + ) + if sends[index - 1]: + p.send(sends[index - 1]) + + self.logger.debug("%s: expecting again: %s", self, patterns) + self.logger.debug( + "%s: got spawned_re: '%s' matching '%s'", + self, + p.match.group(0), + spawned_re, + ) + return p + except pexpect.TIMEOUT: + self.logger.error( + "%s: TIMEOUT looking for spawned_re '%s' expect buffer so far:\n%s", + self, + spawned_re, + indent(p.buffer), + ) + raise + except pexpect.EOF as eoferr: + if p.isalive(): + raise + rc = p.status + before = indent(p.before) + error = CalledProcessError(rc, ac, output=before) + self.logger.error( + "%s: EOF looking for spawned_re '%s' before EOF:\n%s", + self, + spawned_re, + before, + ) + p.close() + raise error from eoferr + + async def shell_spawn( + self, + cmd, + prompt, + expects=(), + sends=(), + use_pty=False, + will_echo=False, + is_bourne=True, + init_newline=False, + **kwargs, + ): + """Create a shell REPL (read-eval-print-loop). + + Args: + cmd: shell and list of args to popen with, or an already open socket + prompt: the REPL prompt to look for, the function returns when seen + expects: a list of regex other than `spawned_re` to look for. Commonly, + "ogin:" or "[Pp]assword:"r. + sends: what to send when an element of `expects` matches. So e.g., the + username or password if thats what corresponding expect matched. Can + be the empty string to send nothing. + is_bourne: if False then do not modify shell prompt for internal + parser friently format, and do not expect continuation prompts. + init_newline: send an initial newline for non-bourne shell spawns, otherwise + expect the prompt simply from running the command + use_pty: true for pty based expect, otherwise uses popen (pipes/files) + will_echo: bash is buggy in that it echo's to non-tty unlike any other + sh/ksh, set this value to true if running back + **kwargs - kwargs passed on the _spawn. + """ + combined_prompt = r"({}|{})".format(re.escape(PEXPECT_PROMPT), prompt) + + assert not is_file_like(cmd) or not use_pty + p = self.spawn( + cmd, + combined_prompt, + expects=expects, + sends=sends, + use_pty=use_pty, + echo=False, + **kwargs, + ) + assert not p.echo + + if not is_bourne: + if init_newline: + p.send("\n") + return ShellWrapper(p, prompt, will_echo=will_echo) + + ps1 = PEXPECT_PROMPT + ps2 = PEXPECT_CONTINUATION_PROMPT + + # Avoid problems when =/usr/bin/env= prints the values + ps1p = ps1[:5] + "${UNSET_V}" + ps1[5:] + ps2p = ps2[:5] + "${UNSET_V}" + ps2[5:] + + ps1 = re.escape(ps1) + ps2 = re.escape(ps2) + + extra = "PAGER=cat; export PAGER; TERM=dumb; unset HISTFILE; set +o emacs +o vi" + pchg = "PS1='{0}' PS2='{1}' PROMPT_COMMAND=''\n".format(ps1p, ps2p) + p.send(pchg) + return ShellWrapper(p, ps1, ps2, extra_init_cmd=extra, will_echo=will_echo) + + def popen(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + a subprocess.Popen object. + """ + return self._popen("popen", cmd, **kwargs)[0] + + def popen_nsonly(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + a subprocess.Popen object. + """ + return self._popen("popen_nsonly", cmd, ns_only=True, **kwargs)[0] + + async def async_popen(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `command` is a string then will be invoked with `bash -c`, otherwise + `command` is a list and will be invoked without a shell. + + Returns: + a asyncio.subprocess.Process object. + """ + p, _ = await self._async_popen("async_popen", cmd, **kwargs) + return p + + async def async_popen_nsonly(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `command` is a string then will be invoked with `bash -c`, otherwise + `command` is a list and will be invoked without a shell. + + Returns: + a asyncio.subprocess.Process object. + """ + p, _ = await self._async_popen( + "async_popen_nsonly", cmd, ns_only=True, **kwargs + ) + return p + + async def async_cleanup_proc(self, p, pid=None): + """Terminate a process started with a popen call. + + Args: + p: return value from :py:`async_popen`, :py:`popen`, et al. + pid: pid to signal instead of p.pid, typically a child of + cmd_p == nsenter. + + Returns: + None on success, the ``p`` if multiple timeouts occur even + after a SIGKILL sent. + """ + if not p: + return None + + if p.returncode is not None: + if isinstance(p, subprocess.Popen): + o, e = p.communicate() + else: + o, e = await p.communicate() + self.logger.debug( + "%s: cmd_p already exited status: %s", self, proc_error(p, o, e) + ) + return None + + if pid is None: + pid = p.pid + + self.logger.debug("%s: terminate process: %s (pid %s)", self, proc_str(p), pid) + try: + # This will SIGHUP and wait a while then SIGKILL and return immediately + await self.cleanup_pid(p.pid, pid) + + # Wait another 2 seconds after the possible SIGKILL above for the + # parent nsenter to cleanup and exit + wait_secs = 2 + if isinstance(p, subprocess.Popen): + o, e = p.communicate(timeout=wait_secs) + else: + o, e = await asyncio.wait_for(p.communicate(), timeout=wait_secs) + self.logger.debug( + "%s: cmd_p exited after kill, status: %s", self, proc_error(p, o, e) + ) + except (asyncio.TimeoutError, subprocess.TimeoutExpired): + self.logger.warning("%s: SIGKILL timeout", self) + return p + except Exception as error: + self.logger.warning( + "%s: kill unexpected exception: %s", self, error, exc_info=True + ) + return p + return None + + @staticmethod + def _cmd_status_input(stdin): + pinput = None + if isinstance(stdin, (bytes, str)): + pinput = stdin + stdin = subprocess.PIPE + return pinput, stdin + + def _cmd_status_finish(self, p, c, ac, o, e, raises, warn): + rc = p.returncode + self.last = (rc, ac, c, o, e) + if rc: + if warn: + self.logger.warning("%s: proc failed: %s", self, proc_error(p, o, e)) + if raises: + # error = Exception("stderr: {}".format(stderr)) + # This annoyingly doesnt' show stderr when printed normally + raise CalledProcessError(rc, ac, o, e) + return rc, o, e + + def _cmd_status(self, cmds, raises=False, warn=True, stdin=None, **kwargs): + """Execute a command.""" + pinput, stdin = Commander._cmd_status_input(stdin) + p, actual_cmd = self._popen("cmd_status", cmds, stdin=stdin, **kwargs) + o, e = p.communicate(pinput) + return self._cmd_status_finish(p, cmds, actual_cmd, o, e, raises, warn) + + async def _async_cmd_status( + self, cmds, raises=False, warn=True, stdin=None, text=None, **kwargs + ): + """Execute a command.""" + pinput, stdin = Commander._cmd_status_input(stdin) + p, actual_cmd = await self._async_popen( + "async_cmd_status", cmds, stdin=stdin, **kwargs + ) + + if text is False: + encoding = None + else: + encoding = kwargs.get("encoding", "utf-8") + + if encoding is not None and isinstance(pinput, str): + pinput = pinput.encode(encoding) + o, e = await p.communicate(pinput) + if encoding is not None: + o = o.decode(encoding) if o is not None else o + e = e.decode(encoding) if e is not None else e + return self._cmd_status_finish(p, cmds, actual_cmd, o, e, raises, warn) + + def _get_cmd_as_list(self, cmd): + """Given a list or string return a list form for execution. + + If `cmd` is a string then the returned list uses bash and looks + like this: ["/bin/bash", "-c", cmd]. Some node types override + this function if they utilize a different shell as to return + a different list of values. + + Args: + cmd: list or string representing the command to execute. + + Returns: + list of commands to execute. + """ + if not isinstance(cmd, str): + cmds = cmd + else: + # Make sure the code doesn't think `cd` will work. + assert not re.match(r"cd(\s*|\s+(\S+))$", cmd) + cmds = ["/bin/bash", "-c", cmd] + return cmds + + def cmd_nostatus(self, cmd, **kwargs): + """Run given command returning output[s]. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + if "stderr" is in kwargs and not equal to subprocess.STDOUT, then + both stdout and stderr are returned, otherwise stderr is combined + with stdout and only stdout is returned. + """ + # + # This method serves as the basis for all derived sync cmd variations, so to + # override sync cmd behavior simply override this function and *not* the other + # variations, unless you are changing only that variation's behavior + # + + # XXX change this back to _cmd_status instead of cmd_status when we + # consolidate and cleanup the container overrides of *cmd_* functions + + cmds = cmd + if "stderr" in kwargs and kwargs["stderr"] != subprocess.STDOUT: + _, o, e = self.cmd_status(cmds, **kwargs) + return o, e + if "stderr" in kwargs: + del kwargs["stderr"] + _, o, _ = self.cmd_status(cmds, stderr=subprocess.STDOUT, **kwargs) + return o + + def cmd_status(self, cmd, **kwargs): + """Run given command returning status and outputs. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + (status, output, error) are returned + status: the returncode of the command. + output: stdout as a string from the command. + error: stderr as a string from the command. + """ + # + # This method serves as the basis for all derived sync cmd variations, so to + # override sync cmd behavior simply override this function and *not* the other + # variations, unless you are changing only that variation's behavior + # + return self._cmd_status(cmd, **kwargs) + + def cmd_raises(self, cmd, **kwargs): + """Execute a command. Raise an exception on errors. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + output: stdout as a string from the command. + + Raises: + CalledProcessError: on non-zero exit status + """ + _, stdout, _ = self._cmd_status(cmd, raises=True, **kwargs) + return stdout + + def cmd_nostatus_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + return self.cmd_nostatus(cmd, ns_only=True, **kwargs) + + def cmd_status_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + return self._cmd_status(cmd, ns_only=True, **kwargs) + + def cmd_raises_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + _, stdout, _ = self._cmd_status(cmd, raises=True, ns_only=True, **kwargs) + return stdout + + async def async_cmd_status(self, cmd, **kwargs): + """Run given command returning status and outputs. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `cmd` is a string then will be invoked with `bash -c`, otherwise + `cmd` is a list and will be invoked without a shell. + + Returns: + (status, output, error) are returned + status: the returncode of the command. + output: stdout as a string from the command. + error: stderr as a string from the command. + """ + # + # This method serves as the basis for all derived async cmd variations, so to + # override async cmd behavior simply override this function and *not* the other + # variations, unless you are changing only that variation's behavior + # + return await self._async_cmd_status(cmd, **kwargs) + + async def async_cmd_nostatus(self, cmd, **kwargs): + """Run given command returning output[s]. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `cmd` is a string then will be invoked with `bash -c`, otherwise + `cmd` is a list and will be invoked without a shell. + + Returns: + if "stderr" is in kwargs and not equal to subprocess.STDOUT, then + both stdout and stderr are returned, otherwise stderr is combined + with stdout and only stdout is returned. + + """ + # XXX change this back to _async_cmd_status instead of cmd_status when we + # consolidate and cleanup the container overrides of *cmd_* functions + + cmds = cmd + if "stderr" in kwargs and kwargs["stderr"] != subprocess.STDOUT: + _, o, e = await self._async_cmd_status(cmds, **kwargs) + return o, e + if "stderr" in kwargs: + del kwargs["stderr"] + _, o, _ = await self._async_cmd_status(cmds, stderr=subprocess.STDOUT, **kwargs) + return o + + async def async_cmd_raises(self, cmd, **kwargs): + """Execute a command. Raise an exception on errors. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `cmd` is a string then will be invoked with `bash -c`, otherwise + `cmd` is a list and will be invoked without a shell. + + Returns: + output: stdout as a string from the command. + + Raises: + CalledProcessError: on non-zero exit status + """ + _, stdout, _ = await self._async_cmd_status(cmd, raises=True, **kwargs) + return stdout + + async def async_cmd_status_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + return await self._async_cmd_status(cmd, ns_only=True, **kwargs) + + async def async_cmd_raises_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + _, stdout, _ = await self._async_cmd_status( + cmd, raises=True, ns_only=True, **kwargs + ) + return stdout + + def cmd_legacy(self, cmd, **kwargs): + """Execute a command with stdout and stderr joined, *IGNORES ERROR*.""" + defaults = {"stderr": subprocess.STDOUT} + defaults.update(kwargs) + _, stdout, _ = self._cmd_status(cmd, raises=False, **defaults) + return stdout + + # Run a command in a new window (gnome-terminal, screen, tmux, xterm) + def run_in_window( + self, + cmd, + wait_for=False, + background=False, + name=None, + title=None, + forcex=False, + new_window=False, + tmux_target=None, + ns_only=False, + ): + """Run a command in a new window (TMUX, Screen or XTerm). + + Args: + cmd: string to execute. + wait_for: True to wait for exit from command or `str` as channel neme to + signal on exit, otherwise False + background: Do not change focus to new window. + title: Title for new pane (tmux) or window (xterm). + name: Name of the new window (tmux) + forcex: Force use of X11. + new_window: Open new window (instead of pane) in TMUX + tmux_target: Target for tmux pane. + + Returns: + the pane/window identifier from TMUX (depends on `new_window`) + """ + channel = None + if isinstance(wait_for, str): + channel = wait_for + elif wait_for is True: + channel = "{}-wait-{}".format(our_pid, Commander.tmux_wait_gen) + Commander.tmux_wait_gen += 1 + + if forcex or ("TMUX" not in os.environ and "STY" not in os.environ): + root_level = False + else: + root_level = True + + # SUDO: The important thing to note is that with all these methods we are + # executing on the users windowing system, so even though we are normally + # running as root, we will not be when the command is dispatched. Also + # in the case of SCREEN and X11 we need to sudo *back* to the user as well + # This is also done by SSHRemote by defualt so we should *not* sudo back + # if we are SSHRemote. + + # XXX need to test ssh in screen + # XXX need to test ssh in Xterm + sudo_path = get_exec_path_host(["sudo"]) + # This first test case seems same as last but using list instead of string? + if self.is_vm and self.use_ssh: # pylint: disable=E1101 + if isinstance(cmd, str): + cmd = shlex.split(cmd) + cmd = ["/usr/bin/env", f"MUNET_NODENAME={self.name}"] + cmd + + # get the ssh cmd + cmd = self._get_pre_cmd(False, True, ns_only=ns_only) + [shlex.join(cmd)] + unet = self.unet # pylint: disable=E1101 + uns_cmd = unet._get_pre_cmd( # pylint: disable=W0212 + False, True, ns_only=True, root_level=root_level + ) + # get the nsenter for munet + nscmd = [ + sudo_path, + *uns_cmd, + *cmd, + ] + else: + # This is the command to execute to be inside the namespace. + # We are getting into trouble with quoting. + # Why aren't we passing in MUNET_RUNDIR? + cmd = f"/usr/bin/env MUNET_NODENAME={self.name} {cmd}" + # We need sudo b/c we are executing as the user inside the window system. + sudo_path = get_exec_path_host(["sudo"]) + nscmd = ( + sudo_path + + " " + + self._get_pre_cmd(True, True, ns_only=ns_only, root_level=root_level) + + " " + + cmd + ) + + if "TMUX" in os.environ and not forcex: + cmd = [get_exec_path_host("tmux")] + if new_window: + cmd.append("new-window") + cmd.append("-P") + if name: + cmd.append("-n") + cmd.append(name) + if tmux_target: + cmd.append("-t") + cmd.append(tmux_target) + else: + cmd.append("split-window") + cmd.append("-P") + cmd.append("-h") + if not tmux_target: + tmux_target = os.getenv("TMUX_PANE", "") + if background: + cmd.append("-d") + if tmux_target: + cmd.append("-t") + cmd.append(tmux_target) + + # nscmd is always added as single string argument + if not isinstance(nscmd, str): + nscmd = shlex.join(nscmd) + if title: + nscmd = f"printf '\033]2;{title}\033\\'; {nscmd}" + if channel: + nscmd = f'trap "tmux wait -S {channel}; exit 0" EXIT; {nscmd}' + cmd.append(nscmd) + + elif "STY" in os.environ and not forcex: + # wait for not supported in screen for now + channel = None + cmd = [get_exec_path_host("screen")] + if not os.path.exists( + "/run/screen/S-{}/{}".format(os.environ["USER"], os.environ["STY"]) + ): + # XXX not appropriate for ssh + cmd = ["sudo", "-Eu", os.environ["SUDO_USER"]] + cmd + + if not isinstance(nscmd, str): + nscmd = shlex.join(nscmd) + cmd.append(nscmd) + elif "DISPLAY" in os.environ: + cmd = [get_exec_path_host("xterm")] + if "SUDO_USER" in os.environ: + # Do this b/c making things work as root with xauth seems hard + cmd = [ + get_exec_path_host("sudo"), + "-Eu", + os.environ["SUDO_USER"], + ] + cmd + if title: + cmd.append("-T") + cmd.append(title) + + cmd.append("-e") + if isinstance(nscmd, str): + cmd.extend(shlex.split(nscmd)) + else: + cmd.extend(nscmd) + + # if channel: + # return self.cmd_raises(cmd, skip_pre_cmd=True) + # else: + p = commander.popen( + cmd, + # skip_pre_cmd=True, + stdin=None, + shell=False, + ) + # We should reap the child and report the error then. + time_mod.sleep(2) + if p.poll() is not None: + self.logger.error("%s: Failed to launch xterm: %s", self, comm_error(p)) + return p + else: + self.logger.error( + "DISPLAY, STY, and TMUX not in environment, can't open window" + ) + raise Exception("Window requestd but TMUX, Screen and X11 not available") + + # pane_info = self.cmd_raises(cmd, skip_pre_cmd=True, ns_only=True).strip() + # We are prepending the nsenter command, so use unet.rootcmd + pane_info = commander.cmd_raises(cmd).strip() + + # Re-adjust the layout + if "TMUX" in os.environ: + cmd = [ + get_exec_path_host("tmux"), + "select-layout", + "-t", + pane_info if not tmux_target else tmux_target, + "tiled", + ] + commander.cmd_status(cmd) + + # Wait here if we weren't handed the channel to wait for + if channel and wait_for is True: + cmd = [get_exec_path_host("tmux"), "wait", channel] + # commander.cmd_status(cmd, skip_pre_cmd=True) + commander.cmd_status(cmd) + + return pane_info + + def delete(self): + """Calls self.async_delete within an exec loop.""" + asyncio.run(self.async_delete()) + + async def _async_delete(self): + """Delete this objects resources. + + This is the actual implementation of the resource cleanup, each class + should cleanup it's own resources, generally catching and reporting, + but not reraising any exceptions for it's own cleanup, then it should + invoke `super()._async_delete() without catching any exceptions raised + therein. See other examples in `base.py` or `native.py` + """ + self.logger.info("%s: deleted", self) + + async def async_delete(self): + """Delete the Commander (or derived object). + + The actual implementation for any class should be in `_async_delete` + new derived classes should look at the documentation for that function. + """ + try: + self.deleting = True + await self._async_delete() + except Exception as error: + self.logger.error("%s: error while deleting: %s", self, error) + + +class InterfaceMixin: + """A mixin class to support interface functionality.""" + + def __init__(self, *args, **kwargs): + # del kwargs # get rid of lint + # logging.warning("InterfaceMixin: args: %s kwargs: %s", args, kwargs) + + self._intf_addrs = defaultdict(lambda: [None, None]) + self.net_intfs = {} + self.next_intf_index = 0 + self.basename = "eth" + # self.basename = name + "-eth" + super().__init__(*args, **kwargs) + + @property + def intfs(self): + return sorted(self._intf_addrs.keys()) + + @property + def networks(self): + return sorted(self.net_intfs.keys()) + + def get_intf_addr(self, ifname, ipv6=False): + if ifname not in self._intf_addrs: + return None + return self._intf_addrs[ifname][bool(ipv6)] + + def set_intf_addr(self, ifname, ifaddr): + ifaddr = ipaddress.ip_interface(ifaddr) + self._intf_addrs[ifname][ifaddr.version == 6] = ifaddr + + def net_addr(self, netname, ipv6=False): + if netname not in self.net_intfs: + return None + return self.get_intf_addr(self.net_intfs[netname], ipv6=ipv6) + + def set_intf_basename(self, basename): + self.basename = basename + + def get_next_intf_name(self): + while True: + ifname = self.basename + str(self.next_intf_index) + self.next_intf_index += 1 + if ifname not in self._intf_addrs: + break + return ifname + + def get_ns_ifname(self, ifname): + """Return a namespace unique interface name. + + This function is primarily overriden by L3QemuVM, IOW by any class + that doesn't create it's own network namespace and will share that + with the root (unet) namespace. + + Args: + ifname: the interface name. + + Returns: + A name unique to the namespace of this object. By defualt the assumption + is the ifname is namespace unique. + """ + return ifname + + def register_interface(self, ifname): + if ifname not in self._intf_addrs: + self._intf_addrs[ifname] = [None, None] + + def register_network(self, netname, ifname): + if netname in self.net_intfs: + assert self.net_intfs[netname] == ifname + else: + self.net_intfs[netname] = ifname + + def get_linux_tc_args(self, ifname, config): + """Get interface constraints (jitter, delay, rate) for linux TC. + + The keys and their values are as follows: + + delay (int): number of microseconds + jitter (int): number of microseconds + jitter-correlation (float): % correlation to previous (default 10%) + loss (float): % of loss + loss-correlation (float): % correlation to previous (default 0%) + rate (int or str): bits per second, string allows for use of + {KMGTKiMiGiTi} prefixes "i" means K == 1024 otherwise K == 1000 + """ + del ifname # unused + + netem_args = "" + + def get_number(c, v, d=None): + if v not in c or c[v] is None: + return d + return convert_number(c[v]) + + delay = get_number(config, "delay") + if delay is not None: + netem_args += f" delay {delay}usec" + + jitter = get_number(config, "jitter") + if jitter is not None: + if not delay: + raise ValueError("jitter but no delay specified") + jitter_correlation = get_number(config, "jitter-correlation", 10) + netem_args += f" {jitter}usec {jitter_correlation}%" + + loss = get_number(config, "loss") + if loss is not None: + loss_correlation = get_number(config, "loss-correlation", 0) + if loss_correlation: + netem_args += f" loss {loss}% {loss_correlation}%" + else: + netem_args += f" loss {loss}%" + + if (o_rate := config.get("rate")) is None: + return netem_args, "" + + # + # This comment is not correct, but is trying to talk through/learn the + # machinery. + # + # tokens arrive at `rate` into token buffer. + # limit - number of bytes that can be queued waiting for tokens + # -or- + # latency - maximum amount of time a packet may sit in TBF queue + # + # So this just allows receiving faster than rate for latency amount of + # time, before dropping. + # + # latency = sizeofbucket(limit) / rate (peakrate?) + # + # 32kbit + # -------- = latency = 320ms + # 100kbps + # + # -but then- + # burst ([token] buffer) the largest number of instantaneous + # tokens available (i.e, bucket size). + + tbf_args = "" + DEFLIMIT = 1518 * 1 + DEFBURST = 1518 * 2 + try: + tc_rate = o_rate["rate"] + tc_rate = convert_number(tc_rate) + limit = convert_number(o_rate.get("limit", DEFLIMIT)) + burst = convert_number(o_rate.get("burst", DEFBURST)) + except (KeyError, TypeError): + tc_rate = convert_number(o_rate) + limit = convert_number(DEFLIMIT) + burst = convert_number(DEFBURST) + tbf_args += f" rate {tc_rate/1000}kbit" + if delay: + # give an extra 1/10 of buffer space to handle delay + tbf_args += f" limit {limit} burst {burst}" + else: + tbf_args += f" limit {limit} burst {burst}" + + return netem_args, tbf_args + + def set_intf_constraints(self, ifname, **constraints): + """Set interface outbound constraints. + + Set outbound constraints (jitter, delay, rate) for an interface. All arguments + may also be passed as a string and will be converted to numerical format. All + arguments are also optional. If not specified then that existing constraint will + be cleared. + + Args: + ifname: the name of the interface + delay (int): number of microseconds. + jitter (int): number of microseconds. + jitter-correlation (float): Percent correlation to previous (default 10%). + loss (float): Percent of loss. + loss-correlation (float): Percent correlation to previous (default 25%). + rate (int): bits per second, string allows for use of + {KMGTKiMiGiTi} prefixes "i" means K == 1024 otherwise K == 1000. + """ + nsifname = self.get_ns_ifname(ifname) + netem_args, tbf_args = self.get_linux_tc_args(nsifname, constraints) + count = 1 + selector = f"root handle {count}:" + if netem_args: + self.cmd_raises( + f"tc qdisc add dev {nsifname} {selector} netem {netem_args}" + ) + count += 1 + selector = f"parent {count-1}: handle {count}" + # Place rate limit after delay otherwise limit/burst too complex + if tbf_args: + self.cmd_raises(f"tc qdisc add dev {nsifname} {selector} tbf {tbf_args}") + + self.cmd_raises(f"tc qdisc show dev {nsifname}") + + +class LinuxNamespace(Commander, InterfaceMixin): + """A linux Namespace. + + An object that creates and executes commands in a linux namespace + """ + + def __init__( + self, + name, + net=True, + mount=True, + uts=True, + cgroup=False, + ipc=False, + pid=False, + time=False, + user=False, + unshare_inline=False, + set_hostname=True, + private_mounts=None, + **kwargs, + ): + """Create a new linux namespace. + + Args: + name: Internal name for the namespace. + net: Create network namespace. + mount: Create network namespace. + uts: Create UTS (hostname) namespace. + cgroup: Create cgroup namespace. + ipc: Create IPC namespace. + pid: Create PID namespace, also mounts new /proc. + time: Create time namespace. + user: Create user namespace, also keeps capabilities. + set_hostname: Set the hostname to `name`, uts must also be True. + private_mounts: List of strings of the form + "[/external/path:]/internal/path. If no external path is specified a + tmpfs is mounted on the internal path. Any paths specified are first + passed to `mkdir -p`. + unshare_inline: Unshare the process itself rather than using a proxy. + logger: Passed to superclass. + """ + # logging.warning("LinuxNamespace: name %s kwargs %s", name, kwargs) + + super().__init__(name, **kwargs) + + unet = self.unet + + self.logger.debug("%s: creating", self) + + self.cwd = os.path.abspath(os.getcwd()) + + self.nsflags = [] + self.ifnetns = {} + self.uflags = 0 + self.p_ns_fds = None + self.p_ns_fnames = None + self.pid_ns = False + self.init_pid = None + self.unshare_inline = unshare_inline + self.nsenter_fork = True + + # + # Collect the namespaces to unshare + # + if hasattr(self, "proc_path") and self.proc_path: # pylint: disable=no-member + pp = Path(self.proc_path) # pylint: disable=no-member + else: + pp = unet.proc_path if unet else Path("/proc") + pp = pp.joinpath("%P%", "ns") + + flags = "" + uflags = 0 + nslist = [] + nsflags = [] + if cgroup: + nselm = "cgroup" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "C" + uflags |= linux.CLONE_NEWCGROUP + if ipc: + nselm = "ipc" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "i" + uflags |= linux.CLONE_NEWIPC + if mount or pid: + # We need a new mount namespace for pid + nselm = "mnt" + nslist.append(nselm) + nsflags.append(f"--mount={pp / nselm}") + mount = True + flags += "m" + uflags |= linux.CLONE_NEWNS + if net: + nselm = "net" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + # if pid: + # os.system(f"touch /tmp/netns-{name}") + # cmd.append(f"--net=/tmp/netns-{name}") + # else: + flags += "n" + uflags |= linux.CLONE_NEWNET + if pid: + self.pid_ns = True + # We look for this b/c the unshare pid will share with /sibn/init + nselm = "pid_for_children" + nslist.append(nselm) + nsflags.append(f"--pid={pp / nselm}") + flags += "p" + uflags |= linux.CLONE_NEWPID + if time: + nselm = "time" + # XXX time_for_children? + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "T" + uflags |= linux.CLONE_NEWTIME + if user: + nselm = "user" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "U" + uflags |= linux.CLONE_NEWUSER + if uts: + nselm = "uts" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "u" + uflags |= linux.CLONE_NEWUTS + + assert flags, "LinuxNamespace with no namespaces requested" + + # Should look path up using resources maybe... + mutini_path = get_our_script_path("mutini") + if not mutini_path: + mutini_path = get_our_script_path("mutini.py") + assert mutini_path + cmd = [mutini_path, f"--unshare-flags={flags}", "-v"] + fname = fsafe_name(self.name) + "-mutini.log" + fname = (unet or self).rundir.joinpath(fname) + stdout = open(fname, "w", encoding="utf-8") + stderr = subprocess.STDOUT + + # + # Save the current namespace info to compare against later + # + + if not unet: + nsdict = {x: os.readlink(f"/proc/self/ns/{x}") for x in nslist} + else: + nsdict = { + x: os.readlink(f"{unet.proc_path}/{unet.pid}/ns/{x}") for x in nslist + } + + # + # (A) Basically we need to save the pid of the unshare call for nsenter. + # + # For `unet is not None` (node case) the level this exists at is based on wether + # unet is using a forking nsenter or not. So if unet.nsenter_fork == True then + # we need the child pid of the p.pid (child of pid returned to us), otherwise + # unet.nsenter_fork == False and we just use p.pid as it will be unshare after + # nsenter exec's it. + # + # For the `unet is None` (unet case) the unshare is at the top level or + # non-existent so we always save the returned p.pid. If we are unshare_inline we + # won't have a __pre_cmd but we can save our child_pid to kill later, otherwise + # we set unet.pid to None b/c there's literally nothing to do. + # + # --------------------------------------------------------------------------- + # Breakdown for nested (non-unet) namespace creation, and what PID + # to use for __pre_cmd nsenter use. + # --------------------------------------------------------------------------- + # + # tl;dr + # - for non-inline unshare: Use BBB with pid_for_children, unless none/none + # #then (AAA) returned + # - for inline unshare: use returned pid (AAA) with pid_for_children + # + # All commands use unet.popen to launch the unshare of mutini or cat. + # mutini for PID unshare, otherwise cat. AAA is the returned pid BBB is the + # child of the returned. + # + # Unshare Variant + # --------------- + # + # Here we are running mutini if we are creating new pid namespace workspace, + # cat otherwise. + # + # [PID+PID] pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA nsenter (forking, from unet) (in unet namespaces -pid) + # BBB - AAA AAA unshare --fork --kill-child (forking) + # CCC 1 BBB CCC mutini (non-forking since it is pid 1 in new namespace) + # + # Use BBB if we use pid_for_children, CCC for all + # + # [PID+none] For non-pid workspace creation (but unet pid) we use cat and pid + # tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA nsenter (forking) (in unet namespaces -pid) + # BBB AAA AAA unshare -> cat (from unshare non-forking) + # + # Use BBB for all + # + # [none+PID] For pid workspace creation (but NOT unet pid) we use mutini and pid + # tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA nsenter -> unshare --fork --kill-child + # BBB 1 AAA AAA mutini (non-forking since it is pid 1 in new namespace) + # + # Use AAA if we use pid_for_children, BBB for all + # + # [none+none] For non-pid workspace and non-pid unet we use cat and pid tree + # looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA nsenter -> unshare -> cat + # + # Use AAA for all, there's no BBB + # + # Inline-Unshare Variant + # ---------------------- + # + # For unshare_inline and new PID namespace we have unshared all but our PID + # namespace, but our children end up in the new namespace so the fork popen + # does is good enough. + # + # [PID+PID] pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA unshare --fork --kill-child (forking) + # BBB 1 AAA BBB mutini + # + # Use AAA if we use pid_for_children, BBB for all + # + # [PID+none] For non-pid workspace creation (but unet pid) we use cat and pid + # tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA unshare -> cat + # + # Use AAA for all + # + # [none+PID] For pid workspace creation (but NOT unet pid) we use mutini and pid + # tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA unshare --fork --kill-child + # BBB 1 AAA BBB mutini + # + # Use AAA if we use pid_for_children, BBB for all + # + # [none+none] For non-pid workspace and non-pid unet we use cat and pid tree + # looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA unshare -> cat + # + # Use AAA for all. + # + # + # --------------------------------------------------------------------------- + # Breakdown for unet namespace creation, and what PID to use for __pre_cmd + # --------------------------------------------------------------------------- + # + # tl;dr: save returned PID or nothing. + # - for non-inline unshare: Use AAA with pid_for_children (returned pid) + # - for inline unshare: no __precmd as the fork in popen is enough. + # + # Use commander to launch the unshare mutini/cat (for PID/none + # workspace PID) for non-inline case. AAA is the returned pid BBB is the child + # of the returned. + # + # Unshare Variant + # --------------- + # + # Here we are running mutini if we are creating new pid namespace workspace, + # cat otherwise. + # + # [PID] for unet pid creation pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA unshare --fork --kill-child (forking) + # BBB 1 AAA BBB mutini + # + # Use AAA if we use pid_for_children, BBB for all + # + # [none] for unet non-pid, pid tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA unshare -> cat + # + # Use AAA for all + # + # Inline-Unshare Variant + # ----------------------- + # + # For unshare_inline and new PID namespace we have unshared all but our PID + # namespace, but our children end up in the new namespace so the fork in popen + # does is good enough. + # + # [PID] for unet pid creation pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA 1 uuu AAA mutini + # + # Save p / p.pid, but don't configure any nsenter, uneeded. + # + # Use nothing as the fork when doing a popen is enough to be in all the right + # namepsaces. + # + # [none] for unet non-pid, pid tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # + # Nothing, no __pre_cmd. + # + # + + self.ppid = os.getppid() + self.unshare_inline = unshare_inline + if unshare_inline: + assert unet is None + self.uflags = uflags + # + # Open file descriptors for current namespaces for later resotration. + # + try: + kversion = [int(x) for x in platform.release().split("-")[0].split(".")] + kvok = kversion[0] > 5 or (kversion[0] == 5 and kversion[1] >= 8) + except ValueError: + kvok = False + if ( + not kvok + or sys.version_info[0] < 3 + or (sys.version_info[0] == 3 and sys.version_info[1] < 9) + ): + # get list of namespace file descriptors before we unshare + self.p_ns_fds = [] + self.p_ns_fnames = [] + tmpflags = uflags + for i in range(0, 64): + v = 1 << i + if (tmpflags & v) == 0: + continue + tmpflags &= ~v + if v in linux.namespace_files: + path = os.path.join("/proc/self", linux.namespace_files[v]) + if os.path.exists(path): + self.p_ns_fds.append(os.open(path, 0)) + self.p_ns_fnames.append(f"{path} -> {os.readlink(path)}") + self.logger.debug( + "%s: saving old namespace fd %s (%s)", + self, + self.p_ns_fnames[-1], + self.p_ns_fds[-1], + ) + if not tmpflags: + break + else: + self.p_ns_fds = None + self.p_ns_fnames = None + self.ppid_fd = linux.pidfd_open(self.ppid) + + self.logger.debug( + "%s: unshare to new namespaces %s", + self, + linux.clone_flag_string(uflags), + ) + + linux.unshare(uflags) + + if not pid: + p = None + self.pid = None + self.nsenter_fork = False + else: + # Need to fork to create the PID namespace, but we need to continue + # running from the parent so that things like pytest work. We'll execute + # a mutini process to manage the child init 1 duties. + # + # We (the parent pid) can no longer create threads, due to that being + # restricted by the kernel. See EINVAL in clone(2). + # + p = commander.popen( + [mutini_path, "-v"], + stdin=subprocess.PIPE, + stdout=stdout, + stderr=stderr, + text=True, + # new session/pgid so signals don't propagate + start_new_session=True, + shell=False, + ) + self.pid = p.pid + self.nsenter_fork = False + else: + # Using cat and a stdin PIPE is nice as it will exit when we do. However, + # we also detach it from the pgid so that signals do not propagate to it. + # This is b/c it would exit early (e.g., ^C) then, at least the main munet + # proc which has no other processes like frr daemons running, will take the + # main network namespace with it, which will remove the bridges and the + # veth pair (because the bridge side veth is deleted). + self.logger.debug("%s: creating namespace process: %s", self, cmd) + + # Use the parent unet process if we have one this will cause us to inherit + # the namespaces correctly even in the non-inline case. + parent = self.unet if self.unet else commander + + p = parent.popen( + cmd, + stdin=subprocess.PIPE, + stdout=stdout, + stderr=stderr, + text=True, + start_new_session=not unet, + shell=False, + ) + + # The pid number returned is in the global pid namespace. For unshare_inline + # this can be unfortunate b/c our /proc has been remounted in our new pid + # namespace and won't contain global pid namespace pids. To solve for this + # we get all the pid values for the process below. + + # See (A) above for when we need the child pid. + self.logger.debug("%s: namespace process: %s", self, proc_str(p)) + self.pid = p.pid + if unet and unet.nsenter_fork: + assert not unet.unshare_inline + # Need child pid of p.pid + pgrep = unet.rootcmd.get_exec_path("pgrep") + # a sing fork was done + child_pid = unet.rootcmd.cmd_raises([pgrep, "-o", "-P", str(p.pid)]) + self.pid = int(child_pid.strip()) + self.logger.debug("%s: child of namespace process: %s", self, pid) + + self.p = p + + # Let's always have a valid value. + if self.pid is None: + self.pid = our_pid + + # + # Let's find all our pids in the nested PID namespaces + # + if unet: + proc_path = unet.proc_path + else: + proc_path = self.proc_path if hasattr(self, "proc_path") else "/proc" + proc_path = f"{proc_path}/{self.pid}" + + pid_status = open(f"{proc_path}/status", "r", encoding="ascii").read() + m = re.search(r"\nNSpid:((?:\t[0-9]+)+)\n", pid_status) + self.pids = [int(x) for x in m.group(1).strip().split("\t")] + assert self.pids[0] == self.pid + + self.logger.debug("%s: namespace scoped pids: %s", self, self.pids) + + # ----------------------------------------------- + # Now let's wait until unshare completes it's job + # ----------------------------------------------- + timeout = Timeout(30) + if self.pid is not None and self.pid != our_pid: + while (not p or not p.poll()) and not timeout.is_expired(): + # check new namespace values against old (nsdict), unshare + # can actually take a bit to complete. + for fname in tuple(nslist): + # self.pid will be the global pid b/c we didn't unshare_inline + nspath = f"{proc_path}/ns/{fname}" + try: + nsf = os.readlink(nspath) + except OSError as error: + self.logger.debug( + "unswitched: error (ok) checking %s: %s", nspath, error + ) + continue + if nsdict[fname] != nsf: + self.logger.debug( + "switched: original %s current %s", nsdict[fname], nsf + ) + nslist.remove(fname) + elif unshare_inline: + logging.warning( + "unshare_inline not unshared %s == %s", nsdict[fname], nsf + ) + else: + self.logger.debug( + "unswitched: current %s elapsed: %s", nsf, timeout.elapsed() + ) + if not nslist: + self.logger.debug( + "all done waiting for unshare after %s", timeout.elapsed() + ) + break + + elapsed = int(timeout.elapsed()) + if elapsed <= 3: + time_mod.sleep(0.1) + else: + self.logger.info( + "%s: unshare taking more than %ss: %s", self, elapsed, nslist + ) + time_mod.sleep(1) + + if p is not None and p.poll(): + self.logger.error("%s: namespace process failed: %s", self, comm_error(p)) + assert p.poll() is None, "unshare failed" + + # + # Setup the pre-command to enter the target namespace from the running munet + # process using self.pid + # + + if pid: + nsenter_fork = True + elif unet and unet.nsenter_fork: + # if unet created a pid namespace we need to enter it since we aren't + # entering a child pid namespace we created for the node. Otherwise + # we have a /proc remounted under unet, but our process is running in + # the root pid namepsace + nselm = "pid_for_children" + nsflags.append(f"--pid={pp / nselm}") + nsenter_fork = True + else: + # We dont need a fork. + nsflags.append("-F") + nsenter_fork = False + + # Save nsenter values if running from root namespace + # we need this for the unshare_inline case when run externally (e.g., from + # within tmux server). + root_nsflags = [x.replace("%P%", str(self.pid)) for x in nsflags] + self.__root_base_pre_cmd = ["/usr/bin/nsenter", *root_nsflags] + self.__root_pre_cmd = list(self.__root_base_pre_cmd) + + if unshare_inline: + assert unet is None + # We have nothing to do here since our process is now in the correct + # namespaces and children will inherit from us, even the PID namespace will + # be corrent b/c commands are run by first forking. + self.nsenter_fork = False + self.nsflags = [] + self.__base_pre_cmd = [] + else: + # We will use nsenter + self.nsenter_fork = nsenter_fork + self.nsflags = nsflags + self.__base_pre_cmd = list(self.__root_base_pre_cmd) + + self.__pre_cmd = list(self.__base_pre_cmd) + + # Always mark new mount namespaces as recursive private + if mount: + # if self.p is None and not pid: + self.cmd_raises_nsonly("mount --make-rprivate /") + + # We need to remount the procfs for the new PID namespace, since we aren't using + # unshare(1) which does that for us. + if pid and unshare_inline: + assert mount + self.cmd_raises_nsonly("mount -t proc proc /proc") + + # We do not want cmd_status in child classes (e.g., container) for + # the remaining setup calls in this __init__ function. + + if net: + # Remount /sys to pickup any changes in the network, but keep root + # /sys/fs/cgroup. This pattern could be made generic and supported for any + # overlapping mounts + if mount: + tmpmnt = f"/tmp/cgm-{self.pid}" + self.cmd_status_nsonly( + f"mkdir {tmpmnt} && mount --rbind /sys/fs/cgroup {tmpmnt}" + ) + rc = o = e = None + for i in range(0, 10): + rc, o, e = self.cmd_status_nsonly( + "mount -t sysfs sysfs /sys", warn=False + ) + if not rc: + break + self.logger.debug( + "got error mounting new sysfs will retry: %s", + cmd_error(rc, o, e), + ) + time_mod.sleep(1) + else: + raise Exception(cmd_error(rc, o, e)) + + self.cmd_status_nsonly( + f"mount --move {tmpmnt} /sys/fs/cgroup && rmdir {tmpmnt}" + ) + + # Original micronet code + # self.cmd_raises_nsonly("mount -t sysfs sysfs /sys") + # self.cmd_raises_nsonly( + # "mount -o rw,nosuid,nodev,noexec,relatime " + # "-t cgroup2 cgroup /sys/fs/cgroup" + # ) + + # Set the hostname to the namespace name + if uts and set_hostname: + self.cmd_status_nsonly("hostname " + self.name) + nroot = subprocess.check_output("hostname") + if unshare_inline or (unet and unet.unshare_inline): + assert ( + root_hostname != nroot + ), f'hostname unchanged from "{nroot}" wanted "{self.name}"' + else: + # Assert that we didn't just change the host hostname + assert ( + root_hostname == nroot + ), f'root hostname "{root_hostname}" changed to "{nroot}"!' + + if private_mounts: + if isinstance(private_mounts, str): + private_mounts = [private_mounts] + for m in private_mounts: + s = m.split(":", 1) + if len(s) == 1: + self.tmpfs_mount(s[0]) + else: + self.bind_mount(s[0], s[1]) + + # this will fail if running inside the namespace with PID + if pid: + o = self.cmd_nostatus_nsonly("ls -l /proc/1/ns") + else: + o = self.cmd_nostatus_nsonly("ls -l /proc/self/ns") + + self.logger.debug("namespaces:\n %s", o) + + # will cache the path, which is important in delete to avoid running a shell + # which can hang during cleanup + self.ip_path = get_exec_path_host("ip") + if net: + self.cmd_status_nsonly([self.ip_path, "link", "set", "lo", "up"]) + + self.logger.info("%s: created", self) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + """Get the pre-user-command values. + + The values returned here should be what is required to cause the user's command + to execute in the correct context (e.g., namespace, container, sshremote). + """ + del kwargs + del ns_only + del use_pty + pre_cmd = self.__root_pre_cmd if root_level else self.__pre_cmd + return shlex.join(pre_cmd) if use_str else list(pre_cmd) + + def tmpfs_mount(self, inner): + self.logger.debug("Mounting tmpfs on %s", inner) + self.cmd_raises("mkdir -p " + inner) + self.cmd_raises("mount -n -t tmpfs tmpfs " + inner) + + def bind_mount(self, outer, inner): + self.logger.debug("Bind mounting %s on %s", outer, inner) + if commander.test("-f", outer): + self.cmd_raises(f"mkdir -p {os.path.dirname(inner)} && touch {inner}") + else: + if not commander.test("-e", outer): + commander.cmd_raises_nsonly(f"mkdir -p {outer}") + self.cmd_raises(f"mkdir -p {inner}") + self.cmd_raises("mount --rbind {} {} ".format(outer, inner)) + + def add_netns(self, ns): + self.logger.debug("Adding network namespace %s", ns) + + if os.path.exists("/run/netns/{}".format(ns)): + self.logger.warning("%s: Removing existing nsspace %s", self, ns) + try: + self.delete_netns(ns) + except Exception as ex: + self.logger.warning( + "%s: Couldn't remove existing nsspace %s: %s", + self, + ns, + str(ex), + exc_info=True, + ) + self.cmd_raises_nsonly([self.ip_path, "netns", "add", ns]) + + def delete_netns(self, ns): + self.logger.debug("Deleting network namespace %s", ns) + self.cmd_raises_nsonly([self.ip_path, "netns", "delete", ns]) + + def set_intf_netns(self, intf, ns, up=False): + # In case a user hard-codes 1 thinking it "resets" + ns = str(ns) + if ns == "1": + ns = str(self.pid) + + self.logger.debug("Moving interface %s to namespace %s", intf, ns) + + cmd = [self.ip_path, "link", "set", intf, "netns", ns] + if up: + cmd.append("up") + self.intf_ip_cmd(intf, cmd) + if ns == str(self.pid): + # If we are returning then remove from dict + if intf in self.ifnetns: + del self.ifnetns[intf] + else: + self.ifnetns[intf] = ns + + def reset_intf_netns(self, intf): + self.logger.debug("Moving interface %s to default namespace", intf) + self.set_intf_netns(intf, str(self.pid)) + + def intf_ip_cmd(self, intf, cmd): + """Run an ip command, considering an interface's possible namespace.""" + if intf in self.ifnetns: + if isinstance(cmd, list): + assert cmd[0].endswith("ip") + cmd[1:1] = ["-n", self.ifnetns[intf]] + else: + assert cmd.startswith("ip ") + cmd = "ip -n " + self.ifnetns[intf] + cmd[2:] + self.cmd_raises_nsonly(cmd) + + def intf_tc_cmd(self, intf, cmd): + """Run a tc command, considering an interface's possible namespace.""" + if intf in self.ifnetns: + if isinstance(cmd, list): + assert cmd[0].endswith("tc") + cmd[1:1] = ["-n", self.ifnetns[intf]] + else: + assert cmd.startswith("tc ") + cmd = "tc -n " + self.ifnetns[intf] + cmd[2:] + self.cmd_raises_nsonly(cmd) + + def set_ns_cwd(self, cwd: Union[str, Path]): + """Common code for changing pre_cmd and pre_nscmd.""" + self.logger.debug("%s: new CWD %s", self, cwd) + self.__root_pre_cmd = self.__root_base_pre_cmd + ["--wd=" + str(cwd)] + if self.__pre_cmd: + self.__pre_cmd = self.__base_pre_cmd + ["--wd=" + str(cwd)] + elif self.unshare_inline: + os.chdir(cwd) + + async def _async_delete(self): + if type(self) == LinuxNamespace: # pylint: disable=C0123 + self.logger.info("%s: deleting", self) + else: + self.logger.debug("%s: LinuxNamespace sub-class deleting", self) + + # Signal pid namespace proc to exit + if ( + (self.p is None or self.p.pid != self.pid) + and self.pid + and self.pid != our_pid + ): + self.logger.debug( + "cleanup pid on separate pid %s from proc pid %s", + self.pid, + self.p.pid if self.p else None, + ) + await self.cleanup_pid(self.pid) + + if self.p is not None: + self.logger.debug("cleanup proc pid %s", self.p.pid) + await self.async_cleanup_proc(self.p) + + # return to the previous namespace, need to do this in case anothe munet + # is being created, especially when it plans to inherit the parent's (host) + # namespace. + if self.uflags: + logging.info("restoring from inline unshare: cwd: %s", os.getcwd()) + # This only works in linux>=5.8 + if self.p_ns_fds is None: + self.logger.debug( + "%s: restoring namespaces %s", + self, + linux.clone_flag_string(self.uflags), + ) + # fd = linux.pidfd_open(self.ppid) + fd = self.ppid_fd + retry = 3 + for i in range(0, retry): + try: + linux.setns(fd, self.uflags) + except OSError as error: + self.logger.warning( + "%s: could not reset to old namespace fd %s: %s", + self, + fd, + error, + ) + if i == retry - 1: + raise + time_mod.sleep(1) + os.close(fd) + else: + while self.p_ns_fds: + fd = self.p_ns_fds.pop() + fname = self.p_ns_fnames.pop() + self.logger.debug( + "%s: restoring namespace from fd %s (%s)", self, fname, fd + ) + retry = 3 + for i in range(0, retry): + try: + linux.setns(fd, 0) + break + except OSError as error: + self.logger.warning( + "%s: could not reset to old namespace fd %s (%s): %s", + self, + fname, + fd, + error, + ) + if i == retry - 1: + raise + time_mod.sleep(1) + os.close(fd) + self.p_ns_fds = None + self.p_ns_fnames = None + logging.info("restored from unshare: cwd: %s", os.getcwd()) + + self.__root_base_pre_cmd = ["/bin/false"] + self.__base_pre_cmd = ["/bin/false"] + self.__root_pre_cmd = ["/bin/false"] + self.__pre_cmd = ["/bin/false"] + + await super()._async_delete() + + +class SharedNamespace(Commander): + """Share another namespace. + + An object that executes commands in an existing pid's linux namespace + """ + + def __init__(self, name, pid=None, nsflags=None, **kwargs): + """Share a linux namespace. + + Args: + name: Internal name for the namespace. + pid: PID of the process to share with. + nsflags: nsenter flags to pass to inherit namespaces from + """ + super().__init__(name, **kwargs) + + self.logger.debug("%s: Creating", self) + + self.cwd = os.path.abspath(os.getcwd()) + self.pid = pid if pid is not None else our_pid + + nsflags = (x.replace("%P%", str(self.pid)) for x in nsflags) if nsflags else [] + self.__base_pre_cmd = ["/usr/bin/nsenter", *nsflags] if nsflags else [] + self.__pre_cmd = self.__base_pre_cmd + self.ip_path = self.get_exec_path("ip") + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + """Get the pre-user-command values. + + The values returned here should be what is required to cause the user's command + to execute in the correct context (e.g., namespace, container, sshremote). + """ + del kwargs + del ns_only + del use_pty + assert not root_level + return shlex.join(self.__pre_cmd) if use_str else list(self.__pre_cmd) + + def set_ns_cwd(self, cwd: Union[str, Path]): + """Common code for changing pre_cmd and pre_nscmd.""" + self.logger.debug("%s: new CWD %s", self, cwd) + self.__pre_cmd = self.__base_pre_cmd + ["--wd=" + str(cwd)] + + +class Bridge(SharedNamespace, InterfaceMixin): + """A linux bridge.""" + + next_ord = 1 + + @classmethod + def _get_next_id(cls): + # Do not use `cls` here b/c that makes the variable class specific + n = Bridge.next_ord + Bridge.next_ord = n + 1 + return n + + def __init__(self, name=None, mtu=None, unet=None, **kwargs): + """Create a linux Bridge.""" + self.id = self._get_next_id() + if not name: + name = "br{}".format(self.id) + + unet_pid = our_pid if unet.pid is None else unet.pid + + super().__init__(name, pid=unet_pid, nsflags=unet.nsflags, unet=unet, **kwargs) + + self.set_intf_basename(self.name + "-e") + + self.mtu = mtu + + self.logger.debug("Bridge: Creating") + + assert len(self.name) <= 16 # Make sure fits in IFNAMSIZE + self.cmd_raises(f"ip link delete {name} || true") + self.cmd_raises(f"ip link add {name} type bridge") + if self.mtu: + self.cmd_raises(f"ip link set {name} mtu {self.mtu}") + self.cmd_raises(f"ip link set {name} up") + + self.logger.debug("%s: Created, Running", self) + + def get_ifname(self, netname): + return self.net_intfs[netname] if netname in self.net_intfs else None + + async def _async_delete(self): + """Stop the bridge (i.e., delete the linux resources).""" + if type(self) == Bridge: # pylint: disable=C0123 + self.logger.info("%s: deleting", self) + else: + self.logger.debug("%s: Bridge sub-class deleting", self) + + rc, o, e = await self.async_cmd_status( + [self.ip_path, "link", "show", self.name], + stdin=subprocess.DEVNULL, + start_new_session=True, + warn=False, + ) + if not rc: + rc, o, e = await self.async_cmd_status( + [self.ip_path, "link", "delete", self.name], + stdin=subprocess.DEVNULL, + start_new_session=True, + warn=False, + ) + if rc: + self.logger.error( + "%s: error deleting bridge %s: %s", + self, + self.name, + cmd_error(rc, o, e), + ) + await super()._async_delete() + + +class BaseMunet(LinuxNamespace): + """Munet.""" + + def __init__( + self, + name="munet", + isolated=True, + pid=True, + rundir=None, + pytestconfig=None, + **kwargs, + ): + """Create a Munet.""" + # logging.warning("BaseMunet: %s", name) + + self.hosts = {} + self.switches = {} + self.links = {} + self.macs = {} + self.rmacs = {} + self.isolated = isolated + + self.cli_server = None + self.cli_sockpath = None + self.cli_histfile = None + self.cli_in_window_cmds = {} + self.cli_run_cmds = {} + + # + # We need a directory for various files + # + if not rundir: + rundir = "/tmp/munet" + self.rundir = Path(rundir) + + # + # Always having a global /proc is required to keep things from exploding + # complexity with nested new pid namespaces.. + # + if pid: + self.proc_path = Path(tempfile.mkdtemp(suffix="-proc", prefix="mu-")) + logging.debug("%s: mounting /proc on proc_path %s", name, self.proc_path) + linux.mount("proc", str(self.proc_path), "proc") + else: + self.proc_path = Path("/proc") + + # + # Now create a root level commander that works regardless of whether we inline + # unshare or not. Save it in the global variable as well + # + + if not self.isolated: + self.rootcmd = commander + elif not pid: + nsflags = ( + f"--mount={self.proc_path / '1/ns/mnt'}", + f"--net={self.proc_path / '1/ns/net'}", + f"--uts={self.proc_path / '1/ns/uts'}", + # f"--ipc={self.proc_path / '1/ns/ipc'}", + # f"--time={self.proc_path / '1/ns/time'}", + # f"--cgroup={self.proc_path / '1/ns/cgroup'}", + ) + self.rootcmd = SharedNamespace("root", pid=1, nsflags=nsflags) + else: + # XXX user + nsflags = ( + # XXX Backing up PID namespace just doesn't work. + # f"--pid={self.proc_path / '1/ns/pid_for_children'}", + f"--mount={self.proc_path / '1/ns/mnt'}", + f"--net={self.proc_path / '1/ns/net'}", + f"--uts={self.proc_path / '1/ns/uts'}", + # f"--ipc={self.proc_path / '1/ns/ipc'}", + # f"--time={self.proc_path / '1/ns/time'}", + # f"--cgroup={self.proc_path / '1/ns/cgroup'}", + ) + self.rootcmd = SharedNamespace("root", pid=1, nsflags=nsflags) + global roothost # pylint: disable=global-statement + + roothost = self.rootcmd + + self.cfgopt = munet_config.ConfigOptionsProxy(pytestconfig) + + super().__init__( + name, mount=True, net=isolated, uts=isolated, pid=pid, unet=None, **kwargs + ) + + # This allows us to cleanup any leftover running munet's + if "MUNET_PID" in os.environ: + if os.environ["MUNET_PID"] != str(our_pid): + logging.error( + "Found env MUNET_PID != our pid %s, instead its %s, changing", + our_pid, + os.environ["MUNET_PID"], + ) + os.environ["MUNET_PID"] = str(our_pid) + + # this is for testing purposes do not use + if not BaseMunet.g_unet: + BaseMunet.g_unet = self + + self.logger.debug("%s: Creating", self) + + def __getitem__(self, key): + if key in self.switches: + return self.switches[key] + return self.hosts[key] + + def add_host(self, name, cls=LinuxNamespace, **kwargs): + """Add a host to munet.""" + self.logger.debug("%s: add_host %s(%s)", self, cls.__name__, name) + + self.hosts[name] = cls(name, unet=self, **kwargs) + + # Create a new mounted FS for tracking nested network namespaces creatd by the + # user with `ip netns add` + + # XXX why is this failing with podman??? + # self.hosts[name].tmpfs_mount("/run/netns") + + return self.hosts[name] + + def add_link(self, node1, node2, if1, if2, mtu=None, **intf_constraints): + """Add a link between switch and node or 2 nodes. + + If constraints are given they are applied to each endpoint. See + `InterfaceMixin::set_intf_constraints()` for more info. + """ + isp2p = False + + try: + name1 = node1.name + except AttributeError: + if node1 in self.switches: + node1 = self.switches[node1] + else: + node1 = self.hosts[node1] + name1 = node1.name + + try: + name2 = node2.name + except AttributeError: + if node2 in self.switches: + node2 = self.switches[node2] + else: + node2 = self.hosts[node2] + name2 = node2.name + + if name1 in self.switches: + assert name2 in self.hosts + elif name2 in self.switches: + assert name1 in self.hosts + name1, name2 = name2, name1 + if1, if2 = if2, if1 + else: + # p2p link + assert name1 in self.hosts + assert name2 in self.hosts + isp2p = True + + lname = "{}:{}-{}:{}".format(name1, if1, name2, if2) + self.logger.debug("%s: add_link %s%s", self, lname, " p2p" if isp2p else "") + self.links[lname] = (name1, if1, name2, if2) + + # And create the veth now. + if isp2p: + lhost, rhost = self.hosts[name1], self.hosts[name2] + lifname = "i1{:x}".format(lhost.pid) + + # Done at root level + nsif1 = lhost.get_ns_ifname(if1) + nsif2 = rhost.get_ns_ifname(if2) + + # Use pids[-1] to get the unet scoped pid for hosts + self.cmd_raises_nsonly( + f"ip link add {lifname} type veth peer name {nsif2}" + f" netns {rhost.pids[-1]}" + ) + self.cmd_raises_nsonly(f"ip link set {lifname} netns {lhost.pids[-1]}") + + lhost.cmd_raises_nsonly("ip link set {} name {}".format(lifname, nsif1)) + if mtu: + lhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif1, mtu)) + lhost.cmd_raises_nsonly("ip link set {} up".format(nsif1)) + lhost.register_interface(if1) + + if mtu: + rhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif2, mtu)) + rhost.cmd_raises_nsonly("ip link set {} up".format(nsif2)) + rhost.register_interface(if2) + else: + switch = self.switches[name1] + rhost = self.hosts[name2] + + nsif1 = switch.get_ns_ifname(if1) + nsif2 = rhost.get_ns_ifname(if2) + + if mtu is None: + mtu = switch.mtu + + if len(nsif1) > 16: + self.logger.error('"%s" len %s > 16', nsif1, len(nsif1)) + elif len(nsif2) > 16: + self.logger.error('"%s" len %s > 16', nsif2, len(nsif2)) + assert len(nsif1) <= 16 and len(nsif2) <= 16 # Make sure fits in IFNAMSIZE + + self.logger.debug("%s: Creating veth pair for link %s", self, lname) + + # Use pids[-1] to get the unet scoped pid for hosts + # switch is already in our namespace so nothing to convert. + self.cmd_raises_nsonly( + f"ip link add {nsif1} type veth peer name {nsif2}" + f" netns {rhost.pids[-1]}" + ) + + if mtu: + # if switch.mtu: + # # the switch interface should match the switch config + # switch.cmd_raises_nsonly( + # "ip link set {} mtu {}".format(if1, switch.mtu) + # ) + switch.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif1, mtu)) + rhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif2, mtu)) + + switch.register_interface(if1) + rhost.register_interface(if2) + rhost.register_network(switch.name, if2) + + switch.cmd_raises_nsonly(f"ip link set {nsif1} master {switch.name}") + + switch.cmd_raises_nsonly(f"ip link set {nsif1} up") + rhost.cmd_raises_nsonly(f"ip link set {nsif2} up") + + # Cache the MAC values, and reverse mapping + self.get_mac(name1, nsif1) + self.get_mac(name2, nsif2) + + # Setup interface constraints if provided + if intf_constraints: + node1.set_intf_constraints(if1, **intf_constraints) + node2.set_intf_constraints(if2, **intf_constraints) + + def add_switch(self, name, cls=Bridge, **kwargs): + """Add a switch to munet.""" + self.logger.debug("%s: add_switch %s(%s)", self, cls.__name__, name) + self.switches[name] = cls(name, unet=self, **kwargs) + return self.switches[name] + + def get_mac(self, name, ifname): + if name in self.hosts: + dev = self.hosts[name] + else: + dev = self.switches[name] + + nsifname = self.get_ns_ifname(ifname) + + if (name, ifname) not in self.macs: + _, output, _ = dev.cmd_status_nsonly("ip -o link show " + nsifname) + m = re.match(".*link/(loopback|ether) ([0-9a-fA-F:]+) .*", output) + mac = m.group(2) + self.macs[(name, ifname)] = mac + self.rmacs[mac] = (name, ifname) + + return self.macs[(name, ifname)] + + async def _delete_link(self, lname): + rname, rif = self.links[lname][2:4] + host = self.hosts[rname] + nsrif = host.get_ns_ifname(rif) + + self.logger.debug("%s: Deleting veth pair for link %s", self, lname) + rc, o, e = await host.async_cmd_status_nsonly( + [self.ip_path, "link", "delete", nsrif], + stdin=subprocess.DEVNULL, + start_new_session=True, + warn=False, + ) + if rc: + self.logger.error("Err del veth pair %s: %s", lname, cmd_error(rc, o, e)) + + async def _delete_links(self): + # for x in self.links: + # await self._delete_link(x) + return await asyncio.gather(*[self._delete_link(x) for x in self.links]) + + async def _async_delete(self): + """Delete the munet topology.""" + # logger = self.logger if False else logging + logger = self.logger + if type(self) == BaseMunet: # pylint: disable=C0123 + logger.info("%s: deleting.", self) + else: + logger.debug("%s: BaseMunet sub-class deleting.", self) + + logger.debug("Deleting links") + try: + await self._delete_links() + except Exception as error: + logger.error("%s: error deleting links: %s", self, error, exc_info=True) + + logger.debug("Deleting hosts and bridges") + try: + # Delete hosts and switches, wait for them all to complete + # even if there is an exception. + htask = [x.async_delete() for x in self.hosts.values()] + stask = [x.async_delete() for x in self.switches.values()] + await asyncio.gather(*htask, *stask, return_exceptions=True) + except Exception as error: + logger.error( + "%s: error deleting hosts and switches: %s", self, error, exc_info=True + ) + + self.links = {} + self.hosts = {} + self.switches = {} + + try: + if self.cli_server: + self.cli_server.cancel() + self.cli_server = None + if self.cli_sockpath: + await self.async_cmd_status( + "rm -rf " + os.path.dirname(self.cli_sockpath) + ) + self.cli_sockpath = None + except Exception as error: + logger.error( + "%s: error cli server or sockpaths: %s", self, error, exc_info=True + ) + + try: + if self.cli_histfile: + readline.write_history_file(self.cli_histfile) + self.cli_histfile = None + except Exception as error: + logger.error( + "%s: error saving history file: %s", self, error, exc_info=True + ) + + # XXX for some reason setns during the delete is changing our dir to /. + cwd = os.getcwd() + + try: + await super()._async_delete() + except Exception as error: + logger.error( + "%s: error deleting parent classes: %s", self, error, exc_info=True + ) + os.chdir(cwd) + + try: + if self.proc_path and str(self.proc_path) != "/proc": + logger.debug("%s: umount, remove proc_path %s", self, self.proc_path) + linux.umount(str(self.proc_path), 0) + os.rmdir(self.proc_path) + except Exception as error: + logger.warning( + "%s: error umount and removing proc_path %s: %s", + self, + self.proc_path, + error, + exc_info=True, + ) + try: + linux.umount(str(self.proc_path), linux.MNT_DETACH) + except Exception as error2: + logger.error( + "%s: error umount with detach proc_path %s: %s", + self, + self.proc_path, + error2, + exc_info=True, + ) + + if BaseMunet.g_unet == self: + BaseMunet.g_unet = None + + +BaseMunet.g_unet = None + +if True: # pylint: disable=using-constant-test + + class ShellWrapper: + """A Read-Execute-Print-Loop (REPL) interface. + + A newline or prompt changing command should be sent to the + spawned child prior to creation as the `prompt` will be `expect`ed + """ + + def __init__( + self, + spawn, + prompt, + continuation_prompt=None, + extra_init_cmd=None, + will_echo=False, + escape_ansi=False, + ): + self.echo = will_echo + self.escape = ( + re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]") if escape_ansi else None + ) + + logging.debug( + 'ShellWraper: XXX prompt "%s" will_echo %s child.echo %s', + prompt, + will_echo, + spawn.echo, + ) + + self.child = spawn + if self.child.echo: + logging.info("Setting child to echo") + self.child.setecho(False) + self.child.waitnoecho() + assert not self.child.echo + + self.prompt = prompt + self.cont_prompt = continuation_prompt + + # Use expect_exact if we can as it should be faster + self.expects = [prompt] + if re.escape(prompt) == prompt and hasattr(self.child, "expect_exact"): + self._expectf = self.child.expect_exact + else: + self._expectf = self.child.expect + if continuation_prompt: + self.expects.append(continuation_prompt) + if re.escape(continuation_prompt) != continuation_prompt: + self._expectf = self.child.expect + + if extra_init_cmd: + self.expect_prompt() + self.child.sendline(extra_init_cmd) + self.expect_prompt() + + def expect_prompt(self, timeout=-1): + return self._expectf(self.expects, timeout=timeout) + + def run_command(self, command, timeout=-1): + """Pexpect REPLWrapper compatible run_command. + + This will split `command` into lines and feed each one to the shell. + + Args: + command: string of commands separated by newlines, a trailing + newline will cause and empty line to be sent. + timeout: pexpect timeout value. + """ + lines = command.splitlines() + if command[-1] == "\n": + lines.append("") + output = "" + index = 0 + for line in lines: + self.child.sendline(line) + index = self.expect_prompt(timeout=timeout) + output += self.child.before + + if index: + if hasattr(self.child, "kill"): + self.child.kill(signal.SIGINT) + else: + self.child.send("\x03") + self.expect_prompt(timeout=30 if self.child.timeout is None else -1) + raise ValueError("Continuation prompt found at end of commands") + + if self.escape: + output = self.escape.sub("", output) + + return output + + def cmd_nostatus(self, cmd, timeout=-1): + r"""Execute a shell command. + + Returns: + (strip/cleaned \r) output + """ + output = self.run_command(cmd, timeout) + output = output.replace("\r\n", "\n") + if self.echo: + # remove the command + idx = output.find(cmd) + if idx == -1: + logging.warning( + "Didn't find command ('%s') in expected output ('%s')", + cmd, + output, + ) + else: + # Remove up to and including the command from the output stream + output = output[idx + len(cmd) :] + + return output.replace("\r", "").strip() + + def cmd_status(self, cmd, timeout=-1): + r"""Execute a shell command. + + Returns: + status and (strip/cleaned \r) output + """ + # Run the command getting the output + output = self.cmd_nostatus(cmd, timeout) + + # Now get the status + scmd = "echo $?" + rcstr = self.run_command(scmd) + rcstr = rcstr.replace("\r\n", "\n") + if self.echo: + # remove the command + idx = rcstr.find(scmd) + if idx == -1: + if self.echo: + logging.warning( + "Didn't find status ('%s') in expected output ('%s')", + scmd, + rcstr, + ) + try: + rc = int(rcstr) + except Exception: + rc = 255 + else: + rcstr = rcstr[idx + len(scmd) :].strip() + try: + rc = int(rcstr) + except ValueError as error: + logging.error( + "%s: error with expected status output: %s: %s", + self, + error, + rcstr, + exc_info=True, + ) + rc = 255 + return rc, output + + def cmd_raises(self, cmd, timeout=-1): + r"""Execute a shell command. + + Returns: + (strip/cleaned \r) ouptut + + Raises: + CalledProcessError: on non-zero exit status + """ + rc, output = self.cmd_status(cmd, timeout) + if rc: + raise CalledProcessError(rc, cmd, output) + return output + + +# --------------------------- +# Root level utility function +# --------------------------- + + +def get_exec_path(binary): + return commander.get_exec_path(binary) + + +def get_exec_path_host(binary): + return commander.get_exec_path(binary) + + +def get_our_script_path(script): + # would be nice to find this w/o using a path lookup + sdir = os.path.dirname(os.path.abspath(__file__)) + spath = os.path.join(sdir, script) + if os.path.exists(spath): + return spath + return get_exec_path(script) + + +commander = Commander("munet") +roothost = None diff --git a/tests/topotests/munet/cleanup.py b/tests/topotests/munet/cleanup.py new file mode 100644 index 0000000000..c641cda685 --- /dev/null +++ b/tests/topotests/munet/cleanup.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 30 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""Provides functionality to cleanup processes on posix systems.""" +import glob +import logging +import os +import signal + + +def get_pids_with_env(has_var, has_val=None): + result = {} + for pidenv in glob.iglob("/proc/*/environ"): + pid = pidenv.split("/")[2] + try: + with open(pidenv, "rb") as rfb: + envlist = [ + x.decode("utf-8").split("=", 1) for x in rfb.read().split(b"\0") + ] + envlist = [[x[0], ""] if len(x) == 1 else x for x in envlist] + envdict = dict(envlist) + if has_var not in envdict: + continue + if has_val is None: + result[pid] = envdict + elif envdict[has_var] == str(has_val): + result[pid] = envdict + except Exception: + # E.g., process exited and files are gone + pass + return result + + +def _kill_piddict(pids_by_upid, sig): + ourpid = str(os.getpid()) + for upid, pids in pids_by_upid: + logging.info("Sending %s to (%s) of munet pid %s", sig, ", ".join(pids), upid) + for pid in pids: + try: + if pid != ourpid: + cmdline = open(f"/proc/{pid}/cmdline", "r", encoding="ascii").read() + cmdline = cmdline.replace("\x00", " ") + logging.info("killing proc %s (%s)", pid, cmdline) + os.kill(int(pid), sig) + except Exception: + pass + + +def _get_our_pids(): + ourpid = str(os.getpid()) + piddict = get_pids_with_env("MUNET_PID", ourpid) + pids = [x for x in piddict if x != ourpid] + if pids: + return {ourpid: pids} + return {} + + +def _get_other_pids(): + piddict = get_pids_with_env("MUNET_PID") + unet_pids = {d["MUNET_PID"] for d in piddict.values()} + pids_by_upid = {p: set() for p in unet_pids} + for pid, envdict in piddict.items(): + unet_pid = envdict["MUNET_PID"] + pids_by_upid[unet_pid].add(pid) + # Filter out any child pid sets whos munet pid is still running + return {x: y for x, y in pids_by_upid.items() if x not in y} + + +def _get_pids_by_upid(ours): + if ours: + return _get_our_pids() + return _get_other_pids() + + +def _cleanup_pids(ours): + pids_by_upid = _get_pids_by_upid(ours).items() + if not pids_by_upid: + return + + t = "current" if ours else "previous" + logging.info("Reaping %s munet processes", t) + + # _kill_piddict(pids_by_upid, signal.SIGTERM) + + # # Give them 5 second to exit cleanly + # logging.info("Waiting up to 5s to allow for clean exit of abandon'd pids") + # for _ in range(0, 5): + # pids_by_upid = _get_pids_by_upid(ours).items() + # if not pids_by_upid: + # return + # time.sleep(1) + + pids_by_upid = _get_pids_by_upid(ours).items() + _kill_piddict(pids_by_upid, signal.SIGKILL) + + +def cleanup_current(): + """Attempt to cleanup preview runs. + + Currently this only scans for old processes. + """ + _cleanup_pids(True) + + +def cleanup_previous(): + """Attempt to cleanup preview runs. + + Currently this only scans for old processes. + """ + _cleanup_pids(False) diff --git a/tests/topotests/munet/cli.py b/tests/topotests/munet/cli.py new file mode 100644 index 0000000000..f58ea99d67 --- /dev/null +++ b/tests/topotests/munet/cli.py @@ -0,0 +1,964 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# July 24 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module that implements a CLI.""" +import argparse +import asyncio +import functools +import logging +import multiprocessing +import os +import pty +import re +import readline +import select +import shlex +import socket +import subprocess +import sys +import tempfile +import termios +import tty + + +try: + from . import linux + from .config import list_to_dict_with_key +except ImportError: + # We cannot use relative imports and still run this module directly as a script, and + # there are some use cases where we want to run this file as a script. + sys.path.append(os.path.dirname(os.path.realpath(__file__))) + import linux + + from config import list_to_dict_with_key + + +ENDMARKER = b"\x00END\x00" + +logger = logging.getLogger(__name__) + + +def lineiter(sock): + s = "" + while True: + sb = sock.recv(256) + if not sb: + return + + s += sb.decode("utf-8") + i = s.find("\n") + if i != -1: + yield s[:i] + s = s[i + 1 :] + + +# Would be nice to convert to async, but really not needed as used +def spawn(unet, host, cmd, iow, ns_only): + if sys.stdin.isatty(): + old_tty = termios.tcgetattr(sys.stdin) + tty.setraw(sys.stdin.fileno()) + + try: + master_fd, slave_fd = pty.openpty() + + ns = unet.hosts[host] if host and host != unet else unet + popenf = ns.popen_nsonly if ns_only else ns.popen + + # use os.setsid() make it run in a new process group, or bash job + # control will not be enabled + p = popenf( + cmd, + # _common_prologue, later in call chain, only does this for use_pty == False + preexec_fn=os.setsid, + stdin=slave_fd, + stdout=slave_fd, + stderr=slave_fd, + universal_newlines=True, + use_pty=True, + # XXX this is actually implementing "run on host" for real + # skip_pre_cmd=ns_only, + ) + iow.write("\r") + iow.flush() + + while p.poll() is None: + r, _, _ = select.select([sys.stdin, master_fd], [], [], 0.25) + if sys.stdin in r: + d = os.read(sys.stdin.fileno(), 10240) + os.write(master_fd, d) + elif master_fd in r: + o = os.read(master_fd, 10240) + if o: + iow.write(o.decode("utf-8")) + iow.flush() + finally: + # restore tty settings back + if sys.stdin.isatty(): + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) + + +def is_host_regex(restr): + return len(restr) > 2 and restr[0] == "/" and restr[-1] == "/" + + +def get_host_regex(restr): + if len(restr) < 3 or restr[0] != "/" or restr[-1] != "/": + return None + return re.compile(restr[1:-1]) + + +def host_in(restr, names): + """Determine if matcher is a regex that matches one of names.""" + if not (regexp := get_host_regex(restr)): + return restr in names + for name in names: + if regexp.fullmatch(name): + return True + return False + + +def expand_host(restr, names): + """Expand name or regexp into list of hosts.""" + hosts = [] + regexp = get_host_regex(restr) + if not regexp: + assert restr in names + hosts.append(restr) + else: + for name in names: + if regexp.fullmatch(name): + hosts.append(name) + return sorted(hosts) + + +def expand_hosts(restrs, names): + """Expand list of host names or regex into list of hosts.""" + hosts = [] + for restr in restrs: + hosts += expand_host(restr, names) + return sorted(hosts) + + +def host_cmd_split(unet, line, toplevel): + all_hosts = set(unet.hosts) + csplit = line.split() + i = 0 + banner = False + for i, e in enumerate(csplit): + if is_re := is_host_regex(e): + banner = True + if not host_in(e, all_hosts): + if not is_re: + break + else: + i += 1 + + if i == 0 and csplit and csplit[0] == "*": + hosts = sorted(all_hosts) + csplit = csplit[1:] + banner = True + elif i == 0 and csplit and csplit[0] == ".": + hosts = [unet] + csplit = csplit[1:] + else: + hosts = expand_hosts(csplit[:i], all_hosts) + csplit = csplit[i:] + + if not hosts and not csplit[:i]: + if toplevel: + hosts = [unet] + else: + hosts = sorted(all_hosts) + banner = True + + if not csplit: + return hosts, "", "", True + + i = line.index(csplit[0]) + i += len(csplit[0]) + return hosts, csplit[0], line[i:].strip(), banner + + +def win_cmd_host_split(unet, cmd, kinds, defall): + if kinds: + all_hosts = { + x for x in unet.hosts if unet.hosts[x].config.get("kind", "") in kinds + } + else: + all_hosts = set(unet.hosts) + + csplit = cmd.split() + i = 0 + for i, e in enumerate(csplit): + if not host_in(e, all_hosts): + if not is_host_regex(e): + break + else: + i += 1 + + if i == 0 and csplit and csplit[0] == "*": + hosts = sorted(all_hosts) + csplit = csplit[1:] + elif i == 0 and csplit and csplit[0] == ".": + hosts = [unet] + csplit = csplit[1:] + else: + hosts = expand_hosts(csplit[:i], all_hosts) + + if not hosts and defall and not csplit[:i]: + hosts = sorted(all_hosts) + + # Filter hosts based on cmd + cmd = " ".join(csplit[i:]) + return hosts, cmd + + +def proc_readline(fd, prompt, histfile): + """Read a line of input from user while running in a sub-process.""" + # How do we change the command though, that's what's displayed in ps normally + linux.set_process_name("Munet CLI") + try: + # For some reason sys.stdin is fileno == 16 and useless + sys.stdin = os.fdopen(0) + histfile = init_history(None, histfile) + line = input(prompt) + readline.write_history_file(histfile) + if line is None: + os.write(fd, b"\n") + os.write(fd, bytes(f":{str(line)}\n", encoding="utf-8")) + except EOFError: + os.write(fd, b"\n") + except KeyboardInterrupt: + os.write(fd, b"I\n") + except Exception as error: + os.write(fd, bytes(f"E{str(error)}\n", encoding="utf-8")) + + +async def async_input_reader(rfd): + """Read a line of input from the user input sub-process pipe.""" + rpipe = os.fdopen(rfd, mode="r") + reader = asyncio.StreamReader() + + def protocol_factory(): + return asyncio.StreamReaderProtocol(reader) + + loop = asyncio.get_event_loop() + transport, _ = await loop.connect_read_pipe(protocol_factory, rpipe) + o = await reader.readline() + transport.close() + + o = o.decode("utf-8").strip() + if not o: + return None + if o[0] == "I": + raise KeyboardInterrupt() + if o[0] == "E": + raise Exception(o[1:]) + assert o[0] == ":" + return o[1:] + + +# +# A lot of work to add async `input` handling without creating a thread. We cannot use +# threads when unshare_inline is used with pid namespace per kernel clone(2) +# restriction. +# +async def async_input(prompt, histfile): + """Asynchronously read a line from the user.""" + rfd, wfd = os.pipe() + p = multiprocessing.Process(target=proc_readline, args=(wfd, prompt, histfile)) + p.start() + logging.debug("started async_input input process: %s", p) + try: + return await async_input_reader(rfd) + finally: + logging.debug("joining async_input input process") + p.join() + + +def make_help_str(unet): + + w = sorted([x if x else "" for x in unet.cli_in_window_cmds]) + ww = unet.cli_in_window_cmds + u = sorted([x if x else "" for x in unet.cli_run_cmds]) + uu = unet.cli_run_cmds + + s = ( + """ +Basic Commands: + cli :: open a secondary CLI window + help :: this help + hosts :: list hosts + quit :: quit the cli + + HOST can be a host or one of the following: + - '*' for all hosts + - '.' for the parent munet + - a regex specified between '/' (e.g., '/rtr.*/') + +New Window Commands:\n""" + + "\n".join([f" {ww[v][0]}\t:: {ww[v][1]}" for v in w]) + + """\nInline Commands:\n""" + + "\n".join([f" {uu[v][0]}\t:: {uu[v][1]}" for v in u]) + + "\n" + ) + return s + + +def get_shcmd(unet, host, kinds, execfmt, ucmd): + if host is None: + h = None + kind = None + elif host is unet or host == "": + h = unet + kind = "" + else: + h = unet.hosts[host] + kind = h.config.get("kind", "") + if kinds and kind not in kinds: + return "" + if not isinstance(execfmt, str): + execfmt = execfmt.get(kind, {}).get("exec", "") + if not execfmt: + return "" + + # Do substitutions for {} in string + numfmt = len(re.findall(r"{\d*}", execfmt)) + if numfmt > 1: + ucmd = execfmt.format(*shlex.split(ucmd)) + elif numfmt: + ucmd = execfmt.format(ucmd) + elif len(re.findall(r"{[a-zA-Z_][0-9a-zA-Z_\.]*}", execfmt)): + if execfmt.endswith('"'): + fstring = "f'''" + execfmt + "'''" + else: + fstring = 'f"""' + execfmt + '"""' + ucmd = eval( # pylint: disable=W0123 + fstring, + globals(), + {"host": h, "unet": unet, "user_input": ucmd}, + ) + else: + # No variable or usercmd substitution at all. + ucmd = execfmt + + # Do substitution for munet variables + ucmd = ucmd.replace("%CONFIGDIR%", str(unet.config_dirname)) + if host is None or host is unet: + ucmd = ucmd.replace("%RUNDIR%", str(unet.rundir)) + return ucmd.replace("%NAME%", ".") + ucmd = ucmd.replace("%RUNDIR%", str(os.path.join(unet.rundir, host))) + if h.mgmt_ip: + ucmd = ucmd.replace("%IPADDR%", str(h.mgmt_ip)) + elif h.mgmt_ip6: + ucmd = ucmd.replace("%IPADDR%", str(h.mgmt_ip6)) + if h.mgmt_ip6: + ucmd = ucmd.replace("%IP6ADDR%", str(h.mgmt_ip6)) + return ucmd.replace("%NAME%", str(host)) + + +async def run_command( + unet, + outf, + line, + execfmt, + banner, + hosts, + toplevel, + kinds, + ns_only=False, + interactive=False, +): + """Runs a command on a set of hosts. + + Runs `execfmt`. Prior to executing the string the following transformations are + performed on it. + + `execfmt` may also be a dictionary of dicitonaries keyed on kind with `exec` holding + the kind's execfmt string. + + - if `{}` is present then `str.format` is called to replace `{}` with any extra + input values after the command and hosts are removed from the input. + - else if any `{digits}` are present then `str.format` is called to replace + `{digits}` with positional args obtained from the addittional user input + first passed to `shlex.split`. + - else f-string style interpolation is performed on the string with + the local variables `host` (the current node object or None), + `unet` (the Munet object), and `user_input` (the additional command input) + defined. + + The output is sent to `outf`. If `ns_only` is True then the `execfmt` is + run using `Commander.cmd_status_nsonly` otherwise it is run with + `Commander.cmd_status`. + """ + if kinds: + logging.info("Filtering hosts to kinds: %s", kinds) + hosts = [x for x in hosts if unet.hosts[x].config.get("kind", "") in kinds] + logging.info("Filtered hosts: %s", hosts) + + if not hosts: + if not toplevel: + return + hosts = [unet] + + # if unknowns := [x for x in hosts if x not in unet.hosts]: + # outf.write("%% Unknown host[s]: %s\n" % ", ".join(unknowns)) + # return + + # if sys.stdin.isatty() and interactive: + if interactive: + for host in hosts: + shcmd = get_shcmd(unet, host, kinds, execfmt, line) + if not shcmd: + continue + if len(hosts) > 1 or banner: + outf.write(f"------ Host: {host} ------\n") + spawn(unet, host if not toplevel else unet, shcmd, outf, ns_only) + if len(hosts) > 1 or banner: + outf.write(f"------- End: {host} ------\n") + outf.write("\n") + return + + aws = [] + for host in hosts: + shcmd = get_shcmd(unet, host, kinds, execfmt, line) + if not shcmd: + continue + if toplevel: + ns = unet + else: + ns = unet.hosts[host] if host and host != unet else unet + if ns_only: + cmdf = ns.async_cmd_status_nsonly + else: + cmdf = ns.async_cmd_status + aws.append(cmdf(shcmd, warn=False, stderr=subprocess.STDOUT)) + + results = await asyncio.gather(*aws, return_exceptions=True) + for host, result in zip(hosts, results): + if isinstance(result, Exception): + o = str(result) + "\n" + rc = -1 + else: + rc, o, _ = result + if len(hosts) > 1 or banner: + outf.write(f"------ Host: {host} ------\n") + if rc: + outf.write(f"*** non-zero exit status: {rc}\n") + outf.write(o) + if len(hosts) > 1 or banner: + outf.write(f"------- End: {host} ------\n") + + +cli_builtins = ["cli", "help", "hosts", "quit"] + + +class Completer: + """A completer class for the CLI.""" + + def __init__(self, unet): + self.unet = unet + + def complete(self, text, state): + line = readline.get_line_buffer() + tokens = line.split() + # print(f"\nXXX: tokens: {tokens} text: '{text}' state: {state}'\n") + + first_token = not tokens or (text and len(tokens) == 1) + + # If we have already have a builtin command we are done + if tokens and tokens[0] in cli_builtins: + return [None] + + cli_run_cmds = set(self.unet.cli_run_cmds.keys()) + top_run_cmds = {x for x in cli_run_cmds if self.unet.cli_run_cmds[x][3]} + cli_run_cmds -= top_run_cmds + cli_win_cmds = set(self.unet.cli_in_window_cmds.keys()) + hosts = set(self.unet.hosts.keys()) + is_window_cmd = bool(tokens) and tokens[0] in cli_win_cmds + done_set = set() + if bool(tokens): + if text: + done_set = set(tokens[:-1]) + else: + done_set = set(tokens) + + # Determine the domain for completions + if not tokens or first_token: + all_cmds = ( + set(cli_builtins) | hosts | cli_run_cmds | cli_win_cmds | top_run_cmds + ) + elif is_window_cmd: + all_cmds = hosts + elif tokens and tokens[0] in top_run_cmds: + # nothing to complete if a top level command + pass + elif not bool(done_set & cli_run_cmds): + all_cmds = hosts | cli_run_cmds + + if not text: + completes = all_cmds + else: + # print(f"\nXXX: all_cmds: {all_cmds} text: '{text}'\n") + completes = {x + " " for x in all_cmds if x.startswith(text)} + + # print(f"\nXXX: completes: {completes} text: '{text}' state: {state}'\n") + # remove any completions already present + completes -= done_set + completes = sorted(completes) + [None] + return completes[state] + + +async def doline( + unet, line, outf, background=False, notty=False +): # pylint: disable=R0911 + + line = line.strip() + m = re.fullmatch(r"^(\S+)(?:\s+(.*))?$", line) + if not m: + return True + + cmd = m.group(1) + nline = m.group(2) if m.group(2) else "" + + if cmd in ("q", "quit"): + return False + + if cmd == "help": + outf.write(make_help_str(unet)) + return True + if cmd in ("h", "hosts"): + outf.write(f"% Hosts:\t{' '.join(sorted(unet.hosts.keys()))}\n") + return True + if cmd == "cli": + await remote_cli( + unet, + "secondary> ", + "Secondary CLI", + background, + ) + return True + + # + # In window commands + # + + if cmd in unet.cli_in_window_cmds: + execfmt, toplevel, kinds, kwargs = unet.cli_in_window_cmds[cmd][2:] + + # if toplevel: + # ucmd = " ".join(nline.split()) + # else: + hosts, ucmd = win_cmd_host_split(unet, nline, kinds, False) + if not hosts: + if not toplevel: + return True + hosts = [unet] + + if isinstance(execfmt, str): + found_brace = "{}" in execfmt + else: + found_brace = False + for d in execfmt.values(): + if "{}" in d["exec"]: + found_brace = True + break + if not found_brace and ucmd and not toplevel: + # CLI command does not expect user command so treat as hosts of which some + # must be unknown + unknowns = [x for x in ucmd.split() if x not in unet.hosts] + outf.write(f"% Unknown host[s]: {' '.join(unknowns)}\n") + return True + + try: + if not hosts and toplevel: + hosts = [unet] + + for host in hosts: + shcmd = get_shcmd(unet, host, kinds, execfmt, ucmd) + if toplevel or host == unet: + unet.run_in_window(shcmd, **kwargs) + else: + unet.hosts[host].run_in_window(shcmd, **kwargs) + except Exception as error: + outf.write(f"% Error: {error}\n") + return True + + # + # Inline commands + # + + toplevel = unet.cli_run_cmds[cmd][3] if cmd in unet.cli_run_cmds else False + # if toplevel: + # logging.debug("top-level: cmd: '%s' nline: '%s'", cmd, nline) + # hosts = None + # banner = False + # else: + + hosts, cmd, nline, banner = host_cmd_split(unet, line, toplevel) + hoststr = "munet" if hosts == [unet] else f"{hosts}" + logging.debug("hosts: '%s' cmd: '%s' nline: '%s'", hoststr, cmd, nline) + + if cmd in unet.cli_run_cmds: + pass + elif "" in unet.cli_run_cmds: + nline = f"{cmd} {nline}" + cmd = "" + else: + outf.write(f"% Unknown command: {cmd} {nline}\n") + return True + + execfmt, toplevel, kinds, ns_only, interactive = unet.cli_run_cmds[cmd][2:] + if interactive and notty: + outf.write("% Error: interactive command must be run from primary CLI\n") + return True + + await run_command( + unet, + outf, + nline, + execfmt, + banner, + hosts, + toplevel, + kinds, + ns_only, + interactive, + ) + + return True + + +async def cli_client(sockpath, prompt="munet> "): + """Implement the user-facing CLI for a remote munet reached by a socket.""" + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.settimeout(10) + sock.connect(sockpath) + + # Go into full non-blocking mode now + sock.settimeout(None) + + print("\n--- Munet CLI Starting ---\n\n") + while True: + line = input(prompt) + if line is None: + return + + # Need to put \n back + line += "\n" + + # Send the CLI command + sock.send(line.encode("utf-8")) + + def bendswith(b, sentinel): + slen = len(sentinel) + return len(b) >= slen and b[-slen:] == sentinel + + # Collect the output + rb = b"" + while not bendswith(rb, ENDMARKER): + lb = sock.recv(4096) + if not lb: + return + rb += lb + + # Remove the marker + rb = rb[: -len(ENDMARKER)] + + # Write the output + sys.stdout.write(rb.decode("utf-8")) + + +async def local_cli(unet, outf, prompt, histfile, background): + """Implement the user-side CLI for local munet.""" + assert unet is not None + completer = Completer(unet) + readline.parse_and_bind("tab: complete") + readline.set_completer(completer.complete) + + print("\n--- Munet CLI Starting ---\n\n") + while True: + try: + line = await async_input(prompt, histfile) + if line is None: + return + + if not await doline(unet, line, outf, background): + return + except KeyboardInterrupt: + outf.write("%% Caught KeyboardInterrupt\nUse ^D or 'quit' to exit") + + +def init_history(unet, histfile): + try: + if histfile is None: + histfile = os.path.expanduser("~/.munet-history.txt") + if not os.path.exists(histfile): + if unet: + unet.cmd("touch " + histfile) + else: + subprocess.run("touch " + histfile, shell=True, check=True) + if histfile: + readline.read_history_file(histfile) + return histfile + except Exception as error: + logging.warning("init_history failed: %s", error) + return None + + +async def cli_client_connected(unet, background, reader, writer): + """Handle CLI commands inside the munet process from a socket.""" + # # Go into full non-blocking mode now + # client.settimeout(None) + logging.debug("cli client connected") + while True: + line = await reader.readline() + if not line: + logging.debug("client closed cli connection") + break + line = line.decode("utf-8").strip() + + class EncodingFile: + """Wrap a writer to encode in utf-8.""" + + def __init__(self, writer): + self.writer = writer + + def write(self, x): + self.writer.write(x.encode("utf-8")) + + def flush(self): + self.writer.flush() + + if not await doline(unet, line, EncodingFile(writer), background, notty=True): + logging.debug("server closing cli connection") + return + + writer.write(ENDMARKER) + await writer.drain() + + +async def remote_cli(unet, prompt, title, background): + """Open a CLI in a new window.""" + try: + if not unet.cli_sockpath: + sockpath = os.path.join(tempfile.mkdtemp("-sockdir", "pty-"), "cli.sock") + ccfunc = functools.partial(cli_client_connected, unet, background) + s = await asyncio.start_unix_server(ccfunc, path=sockpath) + unet.cli_server = asyncio.create_task(s.serve_forever(), name="cli-task") + unet.cli_sockpath = sockpath + logging.info("server created on :\n%s\n", sockpath) + + # Open a new window with a new CLI + python_path = await unet.async_get_exec_path(["python3", "python"]) + us = os.path.realpath(__file__) + cmd = f"{python_path} {us}" + if unet.cli_histfile: + cmd += " --histfile=" + unet.cli_histfile + if prompt: + cmd += f" --prompt='{prompt}'" + cmd += " " + unet.cli_sockpath + unet.run_in_window(cmd, title=title, background=False) + except Exception as error: + logging.error("cli server: unexpected exception: %s", error) + + +def add_cli_in_window_cmd( + unet, name, helpfmt, helptxt, execfmt, toplevel, kinds, **kwargs +): + """Adds a CLI command to the CLI. + + The command `cmd` is added to the commands executable by the user from the CLI. See + `base.Commander.run_in_window` for the arguments that can be passed in `args` and + `kwargs` to this function. + + Args: + unet: unet object + name: command string (no spaces) + helpfmt: format of command to display in help (left side) + helptxt: help string for command (right side) + execfmt: interpreter `cmd` to pass to `host.run_in_window()`, if {} present then + allow for user commands to be entered and inserted. May also be a dict of dict + keyed on kind with sub-key of "exec" providing the `execfmt` string for that + kind. + toplevel: run command in common top-level namespaec not inside hosts + kinds: limit CLI command to nodes which match list of kinds. + **kwargs: keyword args to pass to `host.run_in_window()` + """ + unet.cli_in_window_cmds[name] = (helpfmt, helptxt, execfmt, toplevel, kinds, kwargs) + + +def add_cli_run_cmd( + unet, + name, + helpfmt, + helptxt, + execfmt, + toplevel, + kinds, + ns_only=False, + interactive=False, +): + """Adds a CLI command to the CLI. + + The command `cmd` is added to the commands executable by the user from the CLI. + See `run_command` above in the `doline` function and for the arguments that can + be passed in to this function. + + Args: + unet: unet object + name: command string (no spaces) + helpfmt: format of command to display in help (left side) + helptxt: help string for command (right side) + execfmt: format string to insert user cmds into for execution. May also be a + dict of dict keyed on kind with sub-key of "exec" providing the `execfmt` + string for that kind. + toplevel: run command in common top-level namespaec not inside hosts + kinds: limit CLI command to nodes which match list of kinds. + ns_only: Should execute the command on the host vs in the node namespace. + interactive: Should execute the command inside an allocated pty (interactive) + """ + unet.cli_run_cmds[name] = ( + helpfmt, + helptxt, + execfmt, + toplevel, + kinds, + ns_only, + interactive, + ) + + +def add_cli_config(unet, config): + """Adds CLI commands based on config. + + All exec strings will have %CONFIGDIR%, %NAME% and %RUNDIR% replaced with the + corresponding config directory and the current nodes `name` and `rundir`. + Additionally, the exec string will have f-string style interpolation performed + with the local variables `host` (node object or None), `unet` (Munet object) and + `user_input` (if provided to the CLI command) defined. + + The format of the config dictionary can be seen in the following example. + The first list entry represents the default command because it has no `name` key. + + commands: + - help: "run the given FRR command using vtysh" + format: "[HOST ...] FRR-CLI-COMMAND" + exec: "vtysh -c {}" + ns-only: false # the default + interactive: false # the default + - name: "vtysh" + help: "Open a FRR CLI inside new terminal[s] on the given HOST[s]" + format: "vtysh HOST [HOST ...]" + exec: "vtysh" + new-window: true + - name: "capture" + help: "Capture packets on a given network" + format: "pcap NETWORK" + exec: "tshark -s 9200 -i {0} -w /tmp/capture-{0}.pcap" + new-window: true + top-level: true # run in top-level container namespace, above hosts + + The `new_window` key can also be a dictionary which will be passed as keyward + arguments to the `Commander.run_in_window()` function. + + Args: + unet: unet object + config: dictionary of cli config + """ + for cli_cmd in config.get("commands", []): + name = cli_cmd.get("name", None) + helpfmt = cli_cmd.get("format", "") + helptxt = cli_cmd.get("help", "") + execfmt = list_to_dict_with_key(cli_cmd.get("exec-kind"), "kind") + if not execfmt: + execfmt = cli_cmd.get("exec", "bash -c '{}'") + toplevel = cli_cmd.get("top-level", False) + kinds = cli_cmd.get("kinds", []) + stdargs = (unet, name, helpfmt, helptxt, execfmt, toplevel, kinds) + new_window = cli_cmd.get("new-window", None) + if isinstance(new_window, dict): + add_cli_in_window_cmd(*stdargs, **new_window) + elif bool(new_window): + add_cli_in_window_cmd(*stdargs) + else: + # on-host is deprecated it really implemented "ns-only" + add_cli_run_cmd( + *stdargs, + cli_cmd.get("ns-only", cli_cmd.get("on-host")), + cli_cmd.get("interactive", False), + ) + + +def cli( + unet, + histfile=None, + sockpath=None, + force_window=False, + title=None, + prompt=None, + background=True, +): + asyncio.run( + async_cli(unet, histfile, sockpath, force_window, title, prompt, background) + ) + + +async def async_cli( + unet, + histfile=None, + sockpath=None, + force_window=False, + title=None, + prompt=None, + background=True, +): + if prompt is None: + prompt = "munet> " + + if force_window or not sys.stdin.isatty(): + await remote_cli(unet, prompt, title, background) + + if not unet: + logger.debug("client-cli using sockpath %s", sockpath) + + try: + if sockpath: + await cli_client(sockpath, prompt) + else: + await local_cli(unet, sys.stdout, prompt, histfile, background) + except KeyboardInterrupt: + print("\n...^C exiting CLI") + except EOFError: + pass + except Exception as ex: + logger.critical("cli: got exception: %s", ex, exc_info=True) + raise + + +if __name__ == "__main__": + # logging.basicConfig(level=logging.DEBUG, filename="/tmp/topotests/cli-client.log") + logging.basicConfig(level=logging.DEBUG) + logger = logging.getLogger("cli-client") + logger.info("Start logging cli-client") + + parser = argparse.ArgumentParser() + parser.add_argument("--histfile", help="file to user for history") + parser.add_argument("--prompt", help="prompt string to use") + parser.add_argument("socket", help="path to pair of sockets to communicate over") + cli_args = parser.parse_args() + + cli_prompt = cli_args.prompt if cli_args.prompt else "munet> " + asyncio.run( + async_cli( + None, + cli_args.histfile, + cli_args.socket, + prompt=cli_prompt, + background=False, + ) + ) diff --git a/tests/topotests/munet/compat.py b/tests/topotests/munet/compat.py new file mode 100644 index 0000000000..e82a7d5b77 --- /dev/null +++ b/tests/topotests/munet/compat.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# November 16 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""Provide compatible APIs.""" + + +class PytestConfig: + """Pytest config duck-type-compatible object using argprase args.""" + + class Namespace: + """A namespace defined by a dictionary of values.""" + + def __init__(self, args): + self.args = args + + def __getattr__(self, attr): + return self.args[attr] if attr in self.args else None + + def __init__(self, args): + self.args = vars(args) + self.option = PytestConfig.Namespace(self.args) + + def getoption(self, name, default=None, skip=False): + assert not skip + if name.startswith("--"): + name = name[2:] + name = name.replace("-", "_") + if name in self.args: + return self.args[name] if self.args[name] is not None else default + return default diff --git a/tests/topotests/munet/config.py b/tests/topotests/munet/config.py new file mode 100644 index 0000000000..2870ae615c --- /dev/null +++ b/tests/topotests/munet/config.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# June 25 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2021-2022, LabN Consulting, L.L.C. +# +"""A module that defines common configuration utility functions.""" +import logging + +from collections.abc import Iterable +from copy import deepcopy +from typing import overload + + +def find_with_kv(lst, k, v): + if lst: + for e in lst: + if k in e and e[k] == v: + return e + return {} + + +def find_all_with_kv(lst, k, v): + rv = [] + if lst: + for e in lst: + if k in e and e[k] == v: + rv.append(e) + return rv + + +def find_matching_net_config(name, cconf, oconf): + p = find_all_with_kv(oconf.get("connections", {}), "to", name) + if not p: + return {} + + rname = cconf.get("remote-name", None) + if not rname: + return p[0] + + return find_with_kv(p, "name", rname) + + +def merge_using_key(a, b, k): + # First get a dict of indexes in `a` for the key value of `k` in objects of `a` + m = list(a) + mi = {o[k]: i for i, o in enumerate(m)} + for o in b: + bkv = o[k] + if bkv in mi: + m[mi[bkv]] = o + else: + mi[bkv] = len(m) + m.append(o) + return m + + +def list_to_dict_with_key(lst, k): + """Convert a YANG styl list of objects to dict of objects. + + This function converts a YANG style list of objects (dictionaries) to a plain python + dictionary of objects (dictionaries). The value for the supplied key for each + object is used to store the object in the new diciontary. + + This only works for lists of objects which are keyed on a single contained value. + + Args: + lst: a *list* of python dictionary objects. + k: the key value contained in each dictionary object in the list. + + Returns: + A dictionary of objects (dictionaries). + """ + return {x[k]: x for x in (lst if lst else [])} + + +def config_to_dict_with_key(c, ck, k): + """Convert the config item from a list of objects to dict. + + Use :py:func:`list_to_dict_with_key` to convert the list of objects + at ``c[ck]`` to a dict of the objects using the key ``k``. + + Args: + c: config dictionary + ck: The key identifying the list of objects from ``c``. + k: The key to pass to :py:func:`list_to_dict_with_key`. + + Returns: + A dictionary of objects (dictionaries). + """ + c[ck] = list_to_dict_with_key(c.get(ck, []), k) + return c[ck] + + +@overload +def config_subst(config: str, **kwargs) -> str: + ... + + +@overload +def config_subst(config: Iterable, **kwargs) -> Iterable: + ... + + +def config_subst(config: Iterable, **kwargs) -> Iterable: + if isinstance(config, str): + if "%RUNDIR%/%NAME%" in config: + config = config.replace("%RUNDIR%/%NAME%", "%RUNDIR%") + logging.warning( + "config '%RUNDIR%/%NAME%' should be changed to '%RUNDIR%' only, " + "converting automatically for now." + ) + for name, value in kwargs.items(): + config = config.replace(f"%{name.upper()}%", str(value)) + elif isinstance(config, Iterable): + try: + return {k: config_subst(config[k], **kwargs) for k in config} + except (KeyError, TypeError): + return [config_subst(x, **kwargs) for x in config] + return config + + +def value_merge_deepcopy(s1, s2): + """Merge values using deepcopy. + + Create a deepcopy of the result of merging the values from dicts ``s1`` and ``s2``. + If a key exists in both ``s1`` and ``s2`` the value from ``s2`` is used." + """ + d = {} + for k, v in s1.items(): + if k in s2: + d[k] = deepcopy(s2[k]) + else: + d[k] = deepcopy(v) + return d + + +def merge_kind_config(kconf, config): + mergekeys = kconf.get("merge", []) + config = deepcopy(config) + new = deepcopy(kconf) + for k in new: + if k not in config: + continue + + if k not in mergekeys: + new[k] = config[k] + elif isinstance(new[k], list): + new[k].extend(config[k]) + elif isinstance(new[k], dict): + new[k] = {**new[k], **config[k]} + else: + new[k] = config[k] + for k in config: + if k not in new: + new[k] = config[k] + return new + + +def cli_opt_list(option_list): + if not option_list: + return [] + if isinstance(option_list, str): + return [x for x in option_list.split(",") if x] + return [x for x in option_list if x] + + +def name_in_cli_opt_str(name, option_list): + ol = cli_opt_list(option_list) + return name in ol or "all" in ol + + +class ConfigOptionsProxy: + """Proxy options object to fill in for any missing pytest config.""" + + class DefNoneObject: + """An object that returns None for any attribute access.""" + + def __getattr__(self, attr): + return None + + def __init__(self, pytestconfig=None): + if isinstance(pytestconfig, ConfigOptionsProxy): + self.config = pytestconfig.config + self.option = self.config.option + else: + self.config = pytestconfig + if self.config: + self.option = self.config.option + else: + self.option = ConfigOptionsProxy.DefNoneObject() + + def getoption(self, opt, default=None): + if not self.config: + return default + + try: + value = self.config.getoption(opt) + return value if value is not None else default + except ValueError: + return default + + def get_option(self, opt, default=None): + return self.getoption(opt, default) + + def get_option_list(self, opt): + value = self.get_option(opt, "") + return cli_opt_list(value) + + def name_in_option_list(self, name, opt): + optlist = self.get_option_list(opt) + return "all" in optlist or name in optlist diff --git a/tests/topotests/munet/kinds.yaml b/tests/topotests/munet/kinds.yaml new file mode 100644 index 0000000000..0c278d37c9 --- /dev/null +++ b/tests/topotests/munet/kinds.yaml @@ -0,0 +1,84 @@ +version: 1 +kinds: + - name: frr + cap-add: + # Zebra requires these + - NET_ADMIN + - NET_RAW + - SYS_ADMIN + - AUDIT_WRITE # needed for ssh pty allocation + - name: ceos + init: false + shell: false + merge: ["env"] + # Should we cap-drop some of these in privileged mode? + # ceos kind is special. munet will add args to /sbin/init for each + # environment variable of the form `systemd.setenv=ENVNAME=VALUE` for each + # environment varialbe named ENVNAME with a value of `VALUE`. If cmd: is + # changed to anything but `/sbin/init` munet will not do this. + cmd: /sbin/init + privileged: true + env: + - name: "EOS_PLATFORM" + value: "ceoslab" + - name: "container" + value: "docker" + - name: "ETBA" + value: "4" + - name: "SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT" + value: "1" + - name: "INTFTYPE" + value: "eth" + - name: "MAPETH0" + value: "1" + - name: "MGMT_INTF" + value: "eth0" + - name: "CEOS" + value: "1" + + # cap-add: + # # cEOS requires these, except GNMI still doesn't work + # # - NET_ADMIN + # # - NET_RAW + # # - SYS_ADMIN + # # - SYS_RESOURCE # Required for the CLI + + # All Caps + # - AUDIT_CONTROL + # - AUDIT_READ + # - AUDIT_WRITE + # - BLOCK_SUSPEND + # - CHOWN + # - DAC_OVERRIDE + # - DAC_READ_SEARCH + # - FOWNER + # - FSETID + # - IPC_LOCK + # - IPC_OWNER + # - KILL + # - LEASE + # - LINUX_IMMUTABLE + # - MKNOD + # - NET_ADMIN + # - NET_BIND_SERVICE + # - NET_BROADCAST + # - NET_RAW + # - SETFCAP + # - SETGID + # - SETPCAP + # - SETUID + # - SYSLOG + # - SYS_ADMIN + # - SYS_BOOT + # - SYS_CHROOT + # - SYS_MODULE + # - SYS_NICE + # - SYS_PACCT + # - SYS_PTRACE + # - SYS_RAWIO + # - SYS_RESOURCE + # - SYS_TIME + # - SYS_TTY_CONFIG + # - WAKE_ALARM + # - MAC_ADMIN - Smack project? + # - MAC_OVERRIDE - Smack project? diff --git a/tests/topotests/munet/linux.py b/tests/topotests/munet/linux.py new file mode 100644 index 0000000000..417f74566a --- /dev/null +++ b/tests/topotests/munet/linux.py @@ -0,0 +1,267 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# June 10 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""A module that gives access to linux unshare system call.""" + +import ctypes # pylint: disable=C0415 +import ctypes.util # pylint: disable=C0415 +import errno +import functools +import os + + +libc = None + + +def raise_oserror(enum): + s = errno.errorcode[enum] if enum in errno.errorcode else str(enum) + error = OSError(s) + error.errno = enum + error.strerror = s + raise error + + +def _load_libc(): + global libc # pylint: disable=W0601,W0603 + if libc: + return + lcpath = ctypes.util.find_library("c") + libc = ctypes.CDLL(lcpath, use_errno=True) + + +def pause(): + if not libc: + _load_libc() + libc.pause() + + +MS_RDONLY = 1 +MS_NOSUID = 1 << 1 +MS_NODEV = 1 << 2 +MS_NOEXEC = 1 << 3 +MS_SYNCHRONOUS = 1 << 4 +MS_REMOUNT = 1 << 5 +MS_MANDLOCK = 1 << 6 +MS_DIRSYNC = 1 << 7 +MS_NOSYMFOLLOW = 1 << 8 +MS_NOATIME = 1 << 10 +MS_NODIRATIME = 1 << 11 +MS_BIND = 1 << 12 +MS_MOVE = 1 << 13 +MS_REC = 1 << 14 +MS_SILENT = 1 << 15 +MS_POSIXACL = 1 << 16 +MS_UNBINDABLE = 1 << 17 +MS_PRIVATE = 1 << 18 +MS_SLAVE = 1 << 19 +MS_SHARED = 1 << 20 +MS_RELATIME = 1 << 21 +MS_KERNMOUNT = 1 << 22 +MS_I_VERSION = 1 << 23 +MS_STRICTATIME = 1 << 24 +MS_LAZYTIME = 1 << 25 + + +def mount(source, target, fs, flags=0, options=""): + if not libc: + _load_libc() + libc.mount.argtypes = ( + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_ulong, + ctypes.c_char_p, + ) + fsenc = fs.encode() if fs else None + optenc = options.encode() if options else None + ret = libc.mount(source.encode(), target.encode(), fsenc, flags, optenc) + if ret < 0: + err = ctypes.get_errno() + raise OSError( + err, + f"Error mounting {source} ({fs}) on {target}" + f" with options '{options}': {os.strerror(err)}", + ) + + +# unmout options +MNT_FORCE = 0x1 +MNT_DETACH = 0x2 +MNT_EXPIRE = 0x4 +UMOUNT_NOFOLLOW = 0x8 + + +def umount(target, options): + if not libc: + _load_libc() + libc.umount.argtypes = (ctypes.c_char_p, ctypes.c_uint) + + ret = libc.umount(target.encode(), int(options)) + if ret < 0: + err = ctypes.get_errno() + raise OSError( + err, + f"Error umounting {target} with options '{options}': {os.strerror(err)}", + ) + + +def pidfd_open(pid, flags=0): + if hasattr(os, "pidfd_open") and os.pidfd_open is not pidfd_open: + return os.pidfd_open(pid, flags) # pylint: disable=no-member + + if not libc: + _load_libc() + + try: + pfof = libc.pidfd_open + except AttributeError: + __NR_pidfd_open = 434 + _pidfd_open = libc.syscall + _pidfd_open.restype = ctypes.c_int + _pidfd_open.argtypes = ctypes.c_long, ctypes.c_uint, ctypes.c_uint + pfof = functools.partial(_pidfd_open, __NR_pidfd_open) + + fd = pfof(int(pid), int(flags)) + if fd == -1: + raise_oserror(ctypes.get_errno()) + + return fd + + +if not hasattr(os, "pidfd_open"): + os.pidfd_open = pidfd_open + + +def setns(fd, nstype): # noqa: D402 + """See setns(2) manpage.""" + if not libc: + _load_libc() + + if libc.setns(int(fd), int(nstype)) == -1: + raise_oserror(ctypes.get_errno()) + + +def unshare(flags): # noqa: D402 + """See unshare(2) manpage.""" + if not libc: + _load_libc() + + if libc.unshare(int(flags)) == -1: + raise_oserror(ctypes.get_errno()) + + +CLONE_NEWTIME = 0x00000080 +CLONE_VM = 0x00000100 +CLONE_FS = 0x00000200 +CLONE_FILES = 0x00000400 +CLONE_SIGHAND = 0x00000800 +CLONE_PIDFD = 0x00001000 +CLONE_PTRACE = 0x00002000 +CLONE_VFORK = 0x00004000 +CLONE_PARENT = 0x00008000 +CLONE_THREAD = 0x00010000 +CLONE_NEWNS = 0x00020000 +CLONE_SYSVSEM = 0x00040000 +CLONE_SETTLS = 0x00080000 +CLONE_PARENT_SETTID = 0x00100000 +CLONE_CHILD_CLEARTID = 0x00200000 +CLONE_DETACHED = 0x00400000 +CLONE_UNTRACED = 0x00800000 +CLONE_CHILD_SETTID = 0x01000000 +CLONE_NEWCGROUP = 0x02000000 +CLONE_NEWUTS = 0x04000000 +CLONE_NEWIPC = 0x08000000 +CLONE_NEWUSER = 0x10000000 +CLONE_NEWPID = 0x20000000 +CLONE_NEWNET = 0x40000000 +CLONE_IO = 0x80000000 + +clone_flag_names = { + CLONE_NEWTIME: "CLONE_NEWTIME", + CLONE_VM: "CLONE_VM", + CLONE_FS: "CLONE_FS", + CLONE_FILES: "CLONE_FILES", + CLONE_SIGHAND: "CLONE_SIGHAND", + CLONE_PIDFD: "CLONE_PIDFD", + CLONE_PTRACE: "CLONE_PTRACE", + CLONE_VFORK: "CLONE_VFORK", + CLONE_PARENT: "CLONE_PARENT", + CLONE_THREAD: "CLONE_THREAD", + CLONE_NEWNS: "CLONE_NEWNS", + CLONE_SYSVSEM: "CLONE_SYSVSEM", + CLONE_SETTLS: "CLONE_SETTLS", + CLONE_PARENT_SETTID: "CLONE_PARENT_SETTID", + CLONE_CHILD_CLEARTID: "CLONE_CHILD_CLEARTID", + CLONE_DETACHED: "CLONE_DETACHED", + CLONE_UNTRACED: "CLONE_UNTRACED", + CLONE_CHILD_SETTID: "CLONE_CHILD_SETTID", + CLONE_NEWCGROUP: "CLONE_NEWCGROUP", + CLONE_NEWUTS: "CLONE_NEWUTS", + CLONE_NEWIPC: "CLONE_NEWIPC", + CLONE_NEWUSER: "CLONE_NEWUSER", + CLONE_NEWPID: "CLONE_NEWPID", + CLONE_NEWNET: "CLONE_NEWNET", + CLONE_IO: "CLONE_IO", +} + + +def clone_flag_string(flags): + ns = [v for k, v in clone_flag_names.items() if k & flags] + if ns: + return "|".join(ns) + return "None" + + +namespace_files = { + CLONE_NEWUSER: "ns/user", + CLONE_NEWCGROUP: "ns/cgroup", + CLONE_NEWIPC: "ns/ipc", + CLONE_NEWUTS: "ns/uts", + CLONE_NEWNET: "ns/net", + CLONE_NEWPID: "ns/pid_for_children", + CLONE_NEWNS: "ns/mnt", + CLONE_NEWTIME: "ns/time_for_children", +} + +PR_SET_PDEATHSIG = 1 +PR_GET_PDEATHSIG = 2 +PR_SET_NAME = 15 +PR_GET_NAME = 16 + + +def set_process_name(name): + if not libc: + _load_libc() + + # Why does uncommenting this cause failure? + # libc.prctl.argtypes = ( + # ctypes.c_int, + # ctypes.c_ulong, + # ctypes.c_ulong, + # ctypes.c_ulong, + # ctypes.c_ulong, + # ) + + s = ctypes.create_string_buffer(bytes(name, encoding="ascii")) + sr = ctypes.byref(s) + libc.prctl(PR_SET_NAME, sr, 0, 0, 0) + + +def set_parent_death_signal(signum): + if not libc: + _load_libc() + + # Why does uncommenting this cause failure? + libc.prctl.argtypes = ( + ctypes.c_int, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ) + + libc.prctl(PR_SET_PDEATHSIG, signum, 0, 0, 0) diff --git a/tests/topotests/munet/logconf-mutest.yaml b/tests/topotests/munet/logconf-mutest.yaml new file mode 100644 index 0000000000..b450fb9382 --- /dev/null +++ b/tests/topotests/munet/logconf-mutest.yaml @@ -0,0 +1,84 @@ +version: 1 +formatters: + brief: + format: '%(levelname)5s: %(message)s' + operfmt: + class: munet.mulog.ColorFormatter + format: ' ------| %(message)s' + exec: + format: '%(asctime)s %(levelname)5s: %(name)s: %(message)s' + output: + format: '%(asctime)s %(levelname)5s: OUTPUT: %(message)s' + results: + # format: '%(asctime)s %(levelname)5s: %(message)s' + format: '%(message)s' + +handlers: + console: + level: WARNING + class: logging.StreamHandler + formatter: brief + stream: ext://sys.stderr + info_console: + level: INFO + class: logging.StreamHandler + formatter: brief + stream: ext://sys.stderr + oper_console: + level: DEBUG + class: logging.StreamHandler + formatter: operfmt + stream: ext://sys.stderr + exec: + level: DEBUG + class: logging.FileHandler + formatter: exec + filename: mutest-exec.log + mode: w + output: + level: DEBUG + class: munet.mulog.MultiFileHandler + root_path: "mutest.output" + formatter: output + filename: mutest-output.log + mode: w + results: + level: INFO + class: munet.mulog.MultiFileHandler + root_path: "mutest.results" + new_handler_level: DEBUG + formatter: results + filename: mutest-results.log + mode: w + +root: + level: DEBUG + handlers: [ "console", "exec" ] + +loggers: + # These are some loggers that get used... + # munet: + # level: DEBUG + # propagate: true + # munet.base.commander + # level: DEBUG + # propagate: true + # mutest.error: + # level: DEBUG + # propagate: true + mutest.output: + level: DEBUG + handlers: ["output", "exec"] + propagate: false + mutest.results: + level: DEBUG + handlers: [ "info_console", "exec", "output", "results" ] + # We don't propagate this b/c we want a lower level accept on the console + # Instead we use info_console and exec to cover what root would log to. + propagate: false + # This is used to debug the operation of mutest + mutest.oper: + # Records are emitted at DEBUG so this will normally filter everything + level: INFO + handlers: [ "oper_console" ] + propagate: false diff --git a/tests/topotests/munet/logconf.yaml b/tests/topotests/munet/logconf.yaml new file mode 100644 index 0000000000..430ee2096d --- /dev/null +++ b/tests/topotests/munet/logconf.yaml @@ -0,0 +1,32 @@ +version: 1 +formatters: + brief: + format: '%(asctime)s: %(levelname)s: %(message)s' + precise: + format: '%(asctime)s %(levelname)s: %(name)s: %(message)s' + +handlers: + console: + class: logging.StreamHandler + formatter: brief + level: INFO + stream: ext://sys.stderr + file: + class: logging.FileHandler + formatter: precise + level: DEBUG + filename: munet-exec.log + mode: w + +root: + level: DEBUG + handlers: [ "console", "file" ] + +# these are some loggers that get used. +# loggers: +# munet: +# level: DEBUG +# propagate: true +# munet.base.commander +# level: DEBUG +# propagate: true diff --git a/tests/topotests/munet/mucmd.py b/tests/topotests/munet/mucmd.py new file mode 100644 index 0000000000..5518c6dcfe --- /dev/null +++ b/tests/topotests/munet/mucmd.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# December 5 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A command that allows external command execution inside nodes.""" +import argparse +import json +import os +import subprocess +import sys + +from pathlib import Path + + +def newest_file_in(filename, paths, has_sibling=None): + new = None + newst = None + items = (x for y in paths for x in Path(y).rglob(filename)) + for e in items: + st = os.stat(e) + if has_sibling and not e.parent.joinpath(has_sibling).exists(): + continue + if not new or st.st_mtime_ns > newst.st_mtime_ns: + new = e + newst = st + continue + return new, newst + + +def main(*args): + ap = argparse.ArgumentParser(args) + ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc") + ap.add_argument("node", nargs="?", help="node to enter or run command inside") + ap.add_argument( + "shellcmd", + nargs=argparse.REMAINDER, + help="optional shell-command to execute on NODE", + ) + args = ap.parse_args() + if args.rundir: + configpath = Path(args.rundir).joinpath("config.json") + else: + configpath, _ = newest_file_in( + "config.json", + ["/tmp/munet", "/tmp/mutest", "/tmp/unet-test"], + has_sibling=args.node, + ) + print(f'Using "{configpath}"') + + if not configpath.exists(): + print(f'"{configpath}" not found') + return 1 + rundir = configpath.parent + + nodes = [] + config = json.load(open(configpath, encoding="utf-8")) + nodes = list(config.get("topology", {}).get("nodes", [])) + envcfg = config.get("mucmd", {}).get("env", {}) + + # If args.node is not a node it's part of shellcmd + if args.node and args.node not in nodes: + if args.node != ".": + args.shellcmd[0:0] = [args.node] + args.node = None + + if args.node: + name = args.node + nodedir = rundir.joinpath(name) + if not nodedir.exists(): + print('"{name}" node doesn\'t exist in "{rundir}"') + return 1 + rundir = nodedir + else: + name = "munet" + pidpath = rundir.joinpath("nspid") + pid = open(pidpath, encoding="ascii").read().strip() + + env = {**os.environ} + env["MUNET_NODENAME"] = name + env["MUNET_RUNDIR"] = str(rundir) + + for k in envcfg: + envcfg[k] = envcfg[k].replace("%NAME%", str(name)) + envcfg[k] = envcfg[k].replace("%RUNDIR%", str(rundir)) + + # Can't use -F if it's a new pid namespace + ecmd = "/usr/bin/nsenter" + eargs = [ecmd] + + output = subprocess.check_output(["/usr/bin/nsenter", "--help"], encoding="utf-8") + if " -a," in output: + eargs.append("-a") + else: + # -U doesn't work + for flag in ["-u", "-i", "-m", "-n", "-C", "-T"]: + if f" {flag}," in output: + eargs.append(flag) + eargs.append(f"--pid=/proc/{pid}/ns/pid_for_children") + eargs.append(f"--wd={rundir}") + eargs.extend(["-t", pid]) + eargs += args.shellcmd + # print("Using ", eargs) + return os.execvpe(ecmd, eargs, {**env, **envcfg}) + + +if __name__ == "__main__": + exit_status = main() + sys.exit(exit_status) diff --git a/tests/topotests/munet/mulog.py b/tests/topotests/munet/mulog.py new file mode 100644 index 0000000000..f840eae2d8 --- /dev/null +++ b/tests/topotests/munet/mulog.py @@ -0,0 +1,122 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# December 4 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""Utilities for logging in munet.""" + +import logging + +from pathlib import Path + + +class MultiFileHandler(logging.FileHandler): + """A logging handler that logs to new files based on the logger name. + + The MultiFileHandler operates as a FileHandler with additional functionality. In + addition to logging to the specified logging file MultiFileHandler also creates new + FileHandlers for child loggers based on a root logging name path. + + The ``root_path`` determines when to create a new FileHandler. For each received log + record, ``root_path`` is removed from the logger name of the record if present, and + the resulting channel path (if any) determines the directory for a new log file to + also emit the record to. The new file path is constructed by starting with the + directory ``filename`` resides in, then joining the path determined above after + converting "." to "/" and finally by adding back the basename of ``filename``. + + record logger path => mutest.output.testingfoo + root_path => mutest.output + base filename => /tmp/mutest/mutest-exec.log + new logfile => /tmp/mutest/testingfoo/mutest-exec.log + + All messages are also emitted to the common FileLogger for ``filename``. + + If a log record is from a logger that does not start with ``root_path`` no file is + created and the normal emit occurs. + + Args: + root_path: the logging path of the root level for this handler. + new_handler_level: logging level for newly created handlers + log_dir: the log directory to put log files in. + filename: the base log file. + """ + + def __init__(self, root_path, filename=None, **kwargs): + self.__root_path = root_path + self.__basename = Path(filename).name + if root_path[-1] != ".": + self.__root_path += "." + self.__root_pathlen = len(self.__root_path) + self.__kwargs = kwargs + self.__log_dir = Path(filename).absolute().parent + self.__log_dir.mkdir(parents=True, exist_ok=True) + self.__filenames = {} + self.__added = set() + + if "new_handler_level" not in kwargs: + self.__new_handler_level = logging.NOTSET + else: + new_handler_level = kwargs["new_handler_level"] + del kwargs["new_handler_level"] + self.__new_handler_level = new_handler_level + + super().__init__(filename=filename, **kwargs) + + if self.__new_handler_level is None: + self.__new_handler_level = self.level + + def __log_filename(self, name): + if name in self.__filenames: + return self.__filenames[name] + + if not name.startswith(self.__root_path): + newname = None + else: + newname = name[self.__root_pathlen :] + newname = Path(newname.replace(".", "/")) + newname = self.__log_dir.joinpath(newname) + newname = newname.joinpath(self.__basename) + self.__filenames[name] = newname + + self.__filenames[name] = newname + return newname + + def emit(self, record): + newname = self.__log_filename(record.name) + if newname: + if newname not in self.__added: + self.__added.add(newname) + h = logging.FileHandler(filename=newname, **self.__kwargs) + h.setLevel(self.__new_handler_level) + h.setFormatter(self.formatter) + logging.getLogger(record.name).addHandler(h) + h.emit(record) + super().emit(record) + + +class ColorFormatter(logging.Formatter): + """A formatter that adds color sequences based on level.""" + + def __init__(self, fmt=None, datefmt=None, style="%", **kwargs): + grey = "\x1b[90m" + yellow = "\x1b[33m" + red = "\x1b[31m" + bold_red = "\x1b[31;1m" + reset = "\x1b[0m" + # basefmt = " ------| %(message)s " + + self.formatters = { + logging.DEBUG: logging.Formatter(grey + fmt + reset), + logging.INFO: logging.Formatter(grey + fmt + reset), + logging.WARNING: logging.Formatter(yellow + fmt + reset), + logging.ERROR: logging.Formatter(red + fmt + reset), + logging.CRITICAL: logging.Formatter(bold_red + fmt + reset), + } + # Why are we even bothering? + super().__init__(fmt, datefmt, style, **kwargs) + + def format(self, record): + formatter = self.formatters.get(record.levelno) + return formatter.format(record) diff --git a/tests/topotests/munet/munet-schema.json b/tests/topotests/munet/munet-schema.json new file mode 100644 index 0000000000..a1dcd878dd --- /dev/null +++ b/tests/topotests/munet/munet-schema.json @@ -0,0 +1,654 @@ +{ + "title": "labn-munet-config", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Generated by pyang from module labn-munet-config", + "type": "object", + "properties": { + "cli": { + "type": "object", + "properties": { + "commands": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exec": { + "type": "string" + }, + "exec-kind": { + "type": "array", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string" + }, + "exec": { + "type": "string" + } + } + } + }, + "format": { + "type": "string" + }, + "help": { + "type": "string" + }, + "interactive": { + "type": "boolean" + }, + "kinds": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "new-window": { + "type": "boolean" + }, + "top-level": { + "type": "boolean" + } + } + } + } + } + }, + "kinds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "merge": { + "type": "array", + "items": { + "type": "string" + } + }, + "cap-add": { + "type": "array", + "items": { + "type": "string" + } + }, + "cap-remove": { + "type": "array", + "items": { + "type": "string" + } + }, + "cmd": { + "type": "string" + }, + "cleanup-cmd": { + "type": "string" + }, + "ready-cmd": { + "type": "string" + }, + "image": { + "type": "string" + }, + "server": { + "type": "string" + }, + "server-port": { + "type": "number" + }, + "qemu": { + "type": "object", + "properties": { + "bios": { + "type": "string" + }, + "disk": { + "type": "string" + }, + "kerenel": { + "type": "string" + }, + "initrd": { + "type": "string" + }, + "kvm": { + "type": "boolean" + }, + "ncpu": { + "type": "integer" + }, + "memory": { + "type": "string" + }, + "root": { + "type": "string" + }, + "cmdline-extra": { + "type": "string" + }, + "extra-args": { + "type": "string" + }, + "console": { + "type": "object", + "properties": { + "user": { + "type": "string" + }, + "password": { + "type": "string" + }, + "expects": { + "type": "array", + "items": { + "type": "string" + } + }, + "sends": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeout": { + "type": "integer" + } + } + } + } + }, + "connections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "to": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "ipv6": { + "type": "string" + }, + "name": { + "type": "string" + }, + "hostintf": { + "type": "string" + }, + "physical": { + "type": "string" + }, + "remote-name": { + "type": "string" + }, + "driver": { + "type": "string" + }, + "delay": { + "type": "integer" + }, + "jitter": { + "type": "integer" + }, + "jitter-correlation": { + "type": "string" + }, + "loss": { + "type": "integer" + }, + "loss-correlation": { + "type": "string" + }, + "rate": { + "type": "object", + "properties": { + "rate": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "limit": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "burst": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + } + } + } + } + } + }, + "env": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + }, + "gdb-cmd": { + "type": "string" + }, + "gdb-target-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "gdb-run-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "init": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "mounts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + }, + "tmpfs-size": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + }, + "name": { + "type": "string" + }, + "podman": { + "type": "object", + "properties": { + "extra-args": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "privileged": { + "type": "boolean" + }, + "shell": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "volumes": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "topology": { + "type": "object", + "properties": { + "dns-network": { + "type": "string" + }, + "ipv6-enable": { + "type": "boolean" + }, + "networks-autonumber": { + "type": "boolean" + }, + "networks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "ipv6": { + "type": "string" + } + } + } + }, + "nodes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "kind": { + "type": "string" + }, + "cap-add": { + "type": "array", + "items": { + "type": "string" + } + }, + "cap-remove": { + "type": "array", + "items": { + "type": "string" + } + }, + "cmd": { + "type": "string" + }, + "cleanup-cmd": { + "type": "string" + }, + "ready-cmd": { + "type": "string" + }, + "image": { + "type": "string" + }, + "server": { + "type": "string" + }, + "server-port": { + "type": "number" + }, + "qemu": { + "type": "object", + "properties": { + "bios": { + "type": "string" + }, + "disk": { + "type": "string" + }, + "kerenel": { + "type": "string" + }, + "initrd": { + "type": "string" + }, + "kvm": { + "type": "boolean" + }, + "ncpu": { + "type": "integer" + }, + "memory": { + "type": "string" + }, + "root": { + "type": "string" + }, + "cmdline-extra": { + "type": "string" + }, + "extra-args": { + "type": "string" + }, + "console": { + "type": "object", + "properties": { + "user": { + "type": "string" + }, + "password": { + "type": "string" + }, + "expects": { + "type": "array", + "items": { + "type": "string" + } + }, + "sends": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeout": { + "type": "integer" + } + } + } + } + }, + "connections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "to": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "ipv6": { + "type": "string" + }, + "name": { + "type": "string" + }, + "hostintf": { + "type": "string" + }, + "physical": { + "type": "string" + }, + "remote-name": { + "type": "string" + }, + "driver": { + "type": "string" + }, + "delay": { + "type": "integer" + }, + "jitter": { + "type": "integer" + }, + "jitter-correlation": { + "type": "string" + }, + "loss": { + "type": "integer" + }, + "loss-correlation": { + "type": "string" + }, + "rate": { + "type": "object", + "properties": { + "rate": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "limit": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "burst": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + } + } + } + } + } + }, + "env": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + }, + "gdb-cmd": { + "type": "string" + }, + "gdb-target-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "gdb-run-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "init": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "mounts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + }, + "tmpfs-size": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + }, + "name": { + "type": "string" + }, + "podman": { + "type": "object", + "properties": { + "extra-args": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "privileged": { + "type": "boolean" + }, + "shell": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "volumes": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "version": { + "type": "integer" + } + } +}
\ No newline at end of file diff --git a/tests/topotests/munet/mutest/__main__.py b/tests/topotests/munet/mutest/__main__.py new file mode 100644 index 0000000000..c87031112d --- /dev/null +++ b/tests/topotests/munet/mutest/__main__.py @@ -0,0 +1,445 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# December 2 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""Command to execute mutests.""" + +import asyncio +import logging +import os +import subprocess +import sys +import time + +from argparse import ArgumentParser +from argparse import Namespace +from copy import deepcopy +from pathlib import Path +from typing import Union + +from munet import parser +from munet.base import Bridge +from munet.base import get_event_loop +from munet.mutest import userapi as uapi +from munet.native import L3NodeMixin +from munet.native import Munet +from munet.parser import async_build_topology +from munet.parser import get_config + + +# We want all but critical to fit in 5 characters for alignment +logging.addLevelName(logging.WARNING, "WARN") +root_logger = logging.getLogger("") +exec_formatter = logging.Formatter("%(asctime)s %(levelname)5s: %(name)s: %(message)s") + + +async def get_unet(config: dict, croot: Path, rundir: Path, unshare: bool = False): + """Create and run a new Munet topology. + + The topology is built from the given ``config`` to run inside the path indicated + by ``rundir``. If ``unshare`` is True then the process will unshare into it's + own private namespace. + + Args: + config: a config dictionary obtained from ``munet.parser.get_config``. This + value will be modified and stored in the built ``Munet`` object. + croot: common root of all tests, used to search for ``kinds.yaml`` files. + rundir: the path to the run directory for this topology. + unshare: True to unshare the process into it's own private namespace. + + Yields: + Munet: The constructed and running topology. + """ + tasks = [] + unet = None + try: + try: + unet = await async_build_topology( + config, rundir=str(rundir), unshare_inline=unshare + ) + except Exception as error: + logging.debug("unet build failed: %s", error, exc_info=True) + raise + try: + tasks = await unet.run() + except Exception as error: + logging.debug("unet run failed: %s", error, exc_info=True) + raise + logging.debug("unet topology running") + try: + yield unet + except Exception as error: + logging.error("unet fixture: yield unet unexpected exception: %s", error) + raise + except KeyboardInterrupt: + logging.info("Received keyboard while building topology") + raise + finally: + if unet: + await unet.async_delete() + + # No one ever awaits these so cancel them + logging.debug("unet fixture: cleanup") + for task in tasks: + task.cancel() + + # Reset the class variables so auto number is predictable + logging.debug("unet fixture: resetting ords to 1") + L3NodeMixin.next_ord = 1 + Bridge.next_ord = 1 + + +def common_root(path1: Union[str, Path], path2: Union[str, Path]) -> Path: + """Find the common root between 2 paths. + + Args: + path1: Path + path2: Path + Returns: + Path: the shared root components between ``path1`` and ``path2``. + + Examples: + >>> common_root("/foo/bar/baz", "/foo/bar/zip/zap") + PosixPath('/foo/bar') + >>> common_root("/foo/bar/baz", "/fod/bar/zip/zap") + PosixPath('/') + """ + apath1 = Path(path1).absolute().parts + apath2 = Path(path2).absolute().parts + alen = min(len(apath1), len(apath2)) + common = None + for a, b in zip(apath1[:alen], apath2[:alen]): + if a != b: + break + common = common.joinpath(a) if common else Path(a) + return common + + +async def collect(args: Namespace): + """Collect test files. + + Files must match the pattern ``mutest_*.py``, and their containing + directory must have a munet config file present. This function also changes + the current directory to the common parent of all the tests, and paths are + returned relative to the common directory. + + Args: + args: argparse results + + Returns: + (commondir, tests, configs): where ``commondir`` is the path representing + the common parent directory of all the testsd, ``tests`` is a + dictionary of lists of test files, keyed on their containing directory + path, and ``configs`` is a dictionary of config dictionaries also keyed + on its containing directory path. The directory paths are relative to a + common ancestor. + """ + file_select = args.file_select + upaths = args.paths if args.paths else ["."] + globpaths = set() + for upath in (Path(x) for x in upaths): + if upath.is_file(): + paths = {upath.absolute()} + else: + paths = {x.absolute() for x in Path(upath).rglob(file_select)} + globpaths |= paths + tests = {} + configs = {} + + # Find the common root + # We don't actually need this anymore, the idea was prefix test names + # with uncommon paths elements to automatically differentiate them. + common = None + sortedpaths = [] + for path in sorted(globpaths): + sortedpaths.append(path) + dirpath = path.parent + common = common_root(common, dirpath) if common else dirpath + + ocwd = Path().absolute() + try: + os.chdir(common) + # Work with relative paths to the common directory + for path in (x.relative_to(common) for x in sortedpaths): + dirpath = path.parent + if dirpath not in configs: + try: + configs[dirpath] = get_config(search=[dirpath]) + except FileNotFoundError: + logging.warning( + "Skipping '%s' as munet.{yaml,toml,json} not found in '%s'", + path, + dirpath, + ) + continue + if dirpath not in tests: + tests[dirpath] = [] + tests[dirpath].append(path.absolute()) + finally: + os.chdir(ocwd) + return common, tests, configs + + +async def execute_test( + unet: Munet, + test: Path, + args: Namespace, + test_num: int, + exec_handler: logging.Handler, +) -> (int, int, int, Exception): + """Execute a test case script. + + Using the built and running topology in ``unet`` for targets + execute the test case script file ``test``. + + Args: + unet: a running topology. + test: path to the test case script file. + args: argparse results. + test_num: the number of this test case in the run. + exec_handler: exec file handler to add to test loggers which do not propagate. + """ + test_name = testname_from_path(test) + + # Get test case loggers + logger = logging.getLogger(f"mutest.output.{test_name}") + reslog = logging.getLogger(f"mutest.results.{test_name}") + logger.addHandler(exec_handler) + reslog.addHandler(exec_handler) + + # We need to send an info level log to cause the speciifc handler to be + # created, otherwise all these debug ones don't get through + reslog.info("") + + # reslog.debug("START: %s:%s from %s", test_num, test_name, test.stem) + # reslog.debug("-" * 70) + + targets = dict(unet.hosts.items()) + targets["."] = unet + + tc = uapi.TestCase( + str(test_num), test_name, test, targets, logger, reslog, args.full_summary + ) + passed, failed, e = tc.execute() + + run_time = time.time() - tc.info.start_time + + status = "PASS" if not (failed or e) else "FAIL" + + # Turn off for now + reslog.debug("-" * 70) + reslog.debug( + "stats: %d steps, %d pass, %d fail, %s abort, %4.2fs elapsed", + passed + failed, + passed, + failed, + 1 if e else 0, + run_time, + ) + reslog.debug("-" * 70) + reslog.debug("END: %s %s:%s\n", status, test_num, test_name) + + return passed, failed, e + + +def testname_from_path(path: Path) -> str: + """Return test name based on the path to the test file. + + Args: + path: path to the test file. + + Returns: + str: the name of the test. + """ + return str(Path(path).stem).replace("/", ".") + + +def print_header(reslog, unet): + targets = dict(unet.hosts.items()) + nmax = max(len(x) for x in targets) + nmax = max(nmax, len("TARGET")) + sum_fmt = uapi.TestCase.sum_fmt.format(nmax) + reslog.info(sum_fmt, "NUMBER", "STAT", "TARGET", "TIME", "DESCRIPTION") + reslog.info("-" * 70) + + +async def run_tests(args): + reslog = logging.getLogger("mutest.results") + + common, tests, configs = await collect(args) + results = [] + errlog = logging.getLogger("mutest.error") + reslog = logging.getLogger("mutest.results") + printed_header = False + tnum = 0 + start_time = time.time() + try: + for dirpath in tests: + test_files = tests[dirpath] + for test in test_files: + tnum += 1 + config = deepcopy(configs[dirpath]) + test_name = testname_from_path(test) + rundir = args.rundir.joinpath(test_name) + + # Add an test case exec file handler to the root logger and result + # logger + exec_path = rundir.joinpath("mutest-exec.log") + exec_path.parent.mkdir(parents=True, exist_ok=True) + exec_handler = logging.FileHandler(exec_path, "w") + exec_handler.setFormatter(exec_formatter) + root_logger.addHandler(exec_handler) + + try: + async for unet in get_unet(config, common, rundir): + if not printed_header: + print_header(reslog, unet) + printed_header = True + passed, failed, e = await execute_test( + unet, test, args, tnum, exec_handler + ) + except KeyboardInterrupt as error: + errlog.warning("KeyboardInterrupt while running test %s", test_name) + passed, failed, e = 0, 0, error + raise + except Exception as error: + logging.error( + "Error executing test %s: %s", test, error, exc_info=True + ) + errlog.error( + "Error executing test %s: %s", test, error, exc_info=True + ) + passed, failed, e = 0, 0, error + finally: + # Remove the test case exec file handler form the root logger. + root_logger.removeHandler(exec_handler) + results.append((test_name, passed, failed, e)) + + except KeyboardInterrupt: + pass + + run_time = time.time() - start_time + tnum = 0 + tpassed = 0 + tfailed = 0 + texc = 0 + + spassed = 0 + sfailed = 0 + + for result in results: + _, passed, failed, e = result + tnum += 1 + spassed += passed + sfailed += failed + if e: + texc += 1 + if failed or e: + tfailed += 1 + else: + tpassed += 1 + + reslog.info("") + reslog.info( + "run stats: %s steps, %s pass, %s fail, %s abort, %4.2fs elapsed", + spassed + sfailed, + spassed, + sfailed, + texc, + run_time, + ) + reslog.info("-" * 70) + + tnum = 0 + for result in results: + test_name, passed, failed, e = result + tnum += 1 + s = "FAIL" if failed or e else "PASS" + reslog.info(" %s %s:%s", s, tnum, test_name) + + reslog.info("-" * 70) + reslog.info( + "END RUN: %s test scripts, %s passed, %s failed", tnum, tpassed, tfailed + ) + + return 1 if tfailed else 0 + + +async def async_main(args): + status = 3 + try: + # For some reson we are not catching exceptions raised inside + status = await run_tests(args) + except KeyboardInterrupt: + logging.info("Exiting (async_main), received KeyboardInterrupt in main") + except Exception as error: + logging.info( + "Exiting (async_main), unexpected exception %s", error, exc_info=True + ) + logging.debug("async_main returns %s", status) + return status + + +def main(): + ap = ArgumentParser() + ap.add_argument( + "--dist", + type=int, + nargs="?", + const=-1, + default=0, + action="store", + metavar="NUM-THREADS", + help="Run in parallel, value is num. of threads or no value for auto", + ) + ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc") + ap.add_argument( + "--file-select", default="mutest_*.py", help="shell glob for finding tests" + ) + ap.add_argument("--log-config", help="logging config file (yaml, toml, json, ...)") + ap.add_argument( + "-V", + "--full-summary", + action="store_true", + help="print full summary headers from docstrings", + ) + ap.add_argument( + "-v", dest="verbose", action="count", default=0, help="More -v's, more verbose" + ) + ap.add_argument("paths", nargs="*", help="Paths to collect tests from") + args = ap.parse_args() + + rundir = args.rundir if args.rundir else "/tmp/mutest" + args.rundir = Path(rundir) + os.environ["MUNET_RUNDIR"] = rundir + subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True) + + config = parser.setup_logging(args, config_base="logconf-mutest") + # Grab the exec formatter from the logging config + if fconfig := config.get("formatters", {}).get("exec"): + global exec_formatter # pylint: disable=W291,W0603 + exec_formatter = logging.Formatter( + fconfig.get("format"), fconfig.get("datefmt") + ) + + loop = None + status = 4 + try: + loop = get_event_loop() + status = loop.run_until_complete(async_main(args)) + except KeyboardInterrupt: + logging.info("Exiting (main), received KeyboardInterrupt in main") + except Exception as error: + logging.info("Exiting (main), unexpected exception %s", error, exc_info=True) + finally: + if loop: + loop.close() + + sys.exit(status) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/munet/mutest/userapi.py b/tests/topotests/munet/mutest/userapi.py new file mode 100644 index 0000000000..1df8c0d012 --- /dev/null +++ b/tests/topotests/munet/mutest/userapi.py @@ -0,0 +1,1111 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright 2017, 2022, LabN Consulting, L.L.C. +"""Mutest is a simple send/expect based testing framework. + +This module implements the basic send/expect functionality for mutest. The test +developer first creates a munet topology (:ref:`munet-config`) and then writes test +scripts ("test cases") which are composed of calls to the functions defined below +("steps"). In short these are: + +Send/Expect functions: + + - :py:func:`step` + + - :py:func:`step_json` + + - :py:func:`match_step` + + - :py:func:`match_step_json` + + - :py:func:`wait_step` + + - :py:func:`wait_step_json` + +Control/Utility functions: + + - :py:func:`script_dir` + + - :py:func:`include` + + - :py:func:`log` + + - :py:func:`test` + +Test scripts are located by the :command:`mutest` command by their name. The name of a +test script should take the form ``mutest_TESTNAME.py`` where ``TESTNAME`` is replaced +with a user chosen name for the test case. + +Here's a simple example test script which first checks that a specific forwarding entry +is in the FIB for the IP destination ``10.0.1.1``. Then it checks repeatedly for up to +10 seconds for a second forwarding entry in the FIB for the IP destination ``10.0.2.1``. + +.. code-block:: python + + match_step("r1", 'vtysh -c "show ip fib 10.0.1.1"', "Routing entry for 10.0.1.0/24", + "Check for FIB entry for 10.0.1.1") + + wait_step("r1", + 'vtysh -c "show ip fib 10.0.2.1"', + "Routing entry for 10.0.2.0/24", + desc="Check for FIB entry for 10.0.2.1", + timeout=10) + +Notice that the call arguments can be specified by their correct position in the list or +using keyword names, and they can also be specified over multiple lines if preferred. + +All of the functions are documented and defined below. +""" + +# pylint: disable=global-statement + +import functools +import json +import logging +import pprint +import re +import time + +from pathlib import Path +from typing import Any +from typing import Union + +from deepdiff import DeepDiff as json_cmp + +from munet.base import Commander + + +class TestCaseInfo: + """Object to hold nestable TestCase Results.""" + + def __init__(self, tag: str, name: str, path: Path): + self.path = path.absolute() + self.tag = tag + self.name = name + self.steps = 0 + self.passed = 0 + self.failed = 0 + self.start_time = time.time() + self.step_start_time = self.start_time + self.run_time = None + + def __repr__(self): + return ( + f"TestCaseInfo({self.tag} {self.name} steps {self.steps} " + f"p {self.passed} f {self.failed} path {self.path})" + ) + + +class TestCase: + """A mutest testcase. + + This is normally meant to be used internally by the mutest command to + implement the user API. See README-mutest.org for usage details on the + user API. + + Args: + tag: identity of the test in a run. (x.x...) + name: the name of the test case + path: the test file that is being executed. + targets: a dictionary of objects which implement ``cmd_nostatus(str)`` + output_logger: a logger for output and other messages from the test. + result_logger: a logger to output the results of test steps to. + full_summary: if True then print entire doctstring instead of + only the first line in the results report + + Attributes: + tag: identity of the test in a run + name: the name of the test + targets: dictionary of targets. + + steps: total steps executed so far. + passed: number of passing steps. + failed: number of failing steps. + + last: the last command output. + last_m: the last result of re.search during a matching step on the output with + newlines converted to spaces. + + :meta private: + """ + + # sum_hfmt = "{:5.5s} {:4.4s} {:>6.6s} {}" + # sum_dfmt = "{:5s} {:4.4s} {:^6.6s} {}" + sum_fmt = "%-8.8s %4.4s %{}s %6s %s" + + def __init__( + self, + tag: int, + name: str, + path: Path, + targets: dict, + output_logger: logging.Logger = None, + result_logger: logging.Logger = None, + full_summary: bool = False, + ): + + self.info = TestCaseInfo(tag, name, path) + self.__saved_info = [] + self.__short_doc_header = not full_summary + + self.__space_before_result = False + + # we are only ever in a section once, an include ends a section + # so are never in section+include, and another section ends a + # section, so we don't need __in_section to be save in the + # TestCaseInfo struct. + self.__in_section = False + + self.targets = targets + + self.last = "" + self.last_m = None + + self.rlog = result_logger + self.olog = output_logger + self.logf = functools.partial(self.olog.log, logging.INFO) + + oplog = logging.getLogger("mutest.oper") + self.oplogf = oplog.debug + self.oplogf("new TestCase: tag: %s name: %s path: %s", tag, name, path) + + # find the longerst target name and make target field that wide + nmax = max(len(x) for x in targets) + nmax = max(nmax, len("TARGET")) + self.sum_fmt = TestCase.sum_fmt.format(nmax) + + # Let's keep this out of summary for now + self.rlog.debug(self.sum_fmt, "NUMBER", "STAT", "TARGET", "TIME", "DESCRIPTION") + self.rlog.debug("-" * 70) + + @property + def tag(self): + return self.info.tag + + @property + def name(self): + return self.info.name + + @property + def steps(self): + return self.info.steps + + @property + def passed(self): + return self.info.passed + + @property + def failed(self): + return self.info.failed + + def execute(self): + """Execute the test case. + + :meta private: + """ + assert TestCase.g_tc is None + self.oplogf("execute") + try: + TestCase.g_tc = self + e = self.__exec_script(self.info.path, True, False) + except BaseException: + self.__end_test() + raise + return *self.__end_test(), e + + def __del__(self): + if TestCase.g_tc is self: + logging.error("Internal error, TestCase.__end_test() was not called!") + TestCase.g_tc = None + + def __push_execinfo(self, path: Path): + self.oplogf( + "__push_execinfo: path: %s current top is %s", + path, + pprint.pformat(self.info), + ) + newname = self.name + path.stem + self.info.steps += 1 + self.__saved_info.append(self.info) + tag = f"{self.info.tag}.{self.info.steps}" + self.info = TestCaseInfo(tag, newname, path) + self.oplogf("__push_execinfo: now on top: %s", pprint.pformat(self.info)) + + def __pop_execinfo(self): + # do something with tag? + finished_info = self.info + self.info = self.__saved_info.pop() + self.oplogf(" __pop_execinfo: poppped: %s", pprint.pformat(finished_info)) + self.oplogf(" __pop_execinfo: now on top: %s", pprint.pformat(self.info)) + return finished_info + + def __print_header(self, tag, header, add_newline=False): + # self.olog.info(self.sum_fmt, tag, "", "", "", header) + self.olog.info("== %s ==", f"TEST: {tag}. {header}") + if add_newline: + self.rlog.info("") + self.rlog.info("%s. %s", tag, header) + + def __exec_script(self, path, print_header, add_newline): + + # Below was the original method to avoid the global TestCase + # variable; however, we need global functions so we can import them + # into test scripts. Without imports pylint will complain about undefined + # functions and the resulting christmas tree of warnings is annoying. + # + # pylint: disable=possibly-unused-variable,exec-used,redefined-outer-name + # include = self.include + # log = self.logf + # match_step = self.match_step + # match_step_json = self.match_step_json + # step = self.step + # step_json = self.step_json + # test = self.test + # wait_step = self.wait_step + # wait_step_json = self.wait_step_json + + name = f"{path.stem}{self.tag}" + name = re.sub(r"\W|^(?=\d)", "_", name) + + _ok_result = "marker" + try: + self.oplogf("__exec_script: path %s", path) + script = open(path, "r", encoding="utf-8").read() + + # Load the script into a function. + script = script.strip() + s2 = ( + # f"async def _{name}(ok_result):\n" + f"def _{name}(ok_result):\n" + + " " + + script.replace("\n", "\n ") + + "\n return ok_result\n" + + "\n" + ) + exec(s2) + + # Extract any docstring as a title. + if print_header: + title = locals()[f"_{name}"].__doc__.lstrip() + if self.__short_doc_header and (title := title.lstrip()): + if (idx := title.find("\n")) != -1: + title = title[:idx].strip() + if not title: + title = f"Test from file: {self.info.path.name}" + self.__print_header(self.info.tag, title, add_newline) + self.__space_before_result = False + + # Execute the function. + result = locals()[f"_{name}"](_ok_result) + + # Here's where we can do async in the future if we want. + # result = await locals()[f"_{name}"](_ok_result) + except Exception as error: + logging.error( + "Unexpected exception executing %s: %s", name, error, exc_info=True + ) + return error + else: + if result is not _ok_result: + logging.info("%s returned early, result: %s", name, result) + else: + self.oplogf("__exec_script: name %s completed normally", name) + return None + + def __post_result(self, target, success, rstr, logstr=None): + self.oplogf( + "__post_result: target: %s success %s rstr %s", target, success, rstr + ) + if success: + self.info.passed += 1 + status = "PASS" + outlf = self.logf + reslf = self.rlog.info + else: + self.info.failed += 1 + status = "FAIL" + outlf = self.olog.warning + reslf = self.rlog.warning + + self.info.steps += 1 + if logstr is not None: + outlf("R:%d %s: %s" % (self.steps, status, logstr)) + + run_time = time.time() - self.info.step_start_time + + stepstr = f"{self.tag}.{self.steps}" + rtimes = _delta_time_str(run_time) + + if self.__space_before_result: + self.rlog.info("") + self.__space_before_result = False + + reslf(self.sum_fmt, stepstr, status, target, rtimes, rstr) + + # start counting for next step now + self.info.step_start_time = time.time() + + def __end_test(self) -> (int, int): + """End the test log final results. + + Returns: + number of steps, number passed, number failed, run time. + """ + self.oplogf("__end_test: __in_section: %s", self.__in_section) + if self.__in_section: + self.__end_section() + + passed, failed = self.info.passed, self.info.failed + + # No close for loggers + # self.olog.close() + # self.rlog.close() + self.olog = None + self.rlog = None + + assert ( + TestCase.g_tc == self + ), "TestCase global unexpectedly someon else in __end_test" + TestCase.g_tc = None + + self.info.run_time = time.time() - self.info.start_time + return passed, failed + + def _command( + self, + target: str, + cmd: str, + ) -> str: + """Execute a ``cmd`` and return result. + + Args: + target: the target to execute the command on. + cmd: string to execut on the target. + """ + out = self.targets[target].cmd_nostatus(cmd, warn=False) + self.last = out = out.rstrip() + report = out if out else "<no output>" + self.logf("COMMAND OUTPUT:\n%s", report) + return out + + def _command_json( + self, + target: str, + cmd: str, + ) -> dict: + """Execute a json ``cmd`` and return json result. + + Args: + target: the target to execute the command on. + cmd: string to execut on the target. + """ + out = self.targets[target].cmd_nostatus(cmd, warn=False) + self.last = out = out.rstrip() + try: + js = json.loads(out) + except Exception as error: + js = {} + self.olog.warning( + "JSON load failed. Check command output is in JSON format: %s", + error, + ) + self.logf("COMMAND OUTPUT:\n%s", out) + return js + + def _match_command( + self, + target: str, + cmd: str, + match: str, + expect_fail: bool, + flags: int, + ) -> (bool, Union[str, list]): + """Execute a ``cmd`` and check result. + + Args: + target: the target to execute the command on. + cmd: string to execute on the target. + match: regex to ``re.search()`` for in output. + expect_fail: if True then succeed when the regexp doesn't match. + flags: python regex flags to modify matching behavior + + Returns: + (success, matches): if the match fails then "matches" will be None, + otherwise if there were matching groups then groups() will be returned in + ``matches`` otherwise group(0) (i.e., the matching text). + """ + out = self._command(target, cmd) + search = re.search(match, out, flags) + self.last_m = search + if search is None: + success = expect_fail + ret = None + else: + success = not expect_fail + ret = search.groups() + if not ret: + ret = search.group(0) + + level = logging.DEBUG if success else logging.WARNING + self.olog.log(level, "matched:%s:", ret) + return success, ret + + def _match_command_json( + self, + target: str, + cmd: str, + match: Union[str, dict], + expect_fail: bool, + ) -> Union[str, dict]: + """Execute a json ``cmd`` and check result. + + Args: + target: the target to execute the command on. + cmd: string to execut on the target. + match: A json ``str`` or object (``dict``) to compare against the json + output from ``cmd``. + expect_fail: if True then succeed when the json doesn't match. + """ + js = self._command_json(target, cmd) + try: + expect = json.loads(match) + except Exception as error: + expect = {} + self.olog.warning( + "JSON load failed. Check match value is in JSON format: %s", error + ) + + if json_diff := json_cmp(expect, js): + success = expect_fail + if not success: + self.logf("JSON DIFF:%s:" % json_diff) + return success, json_diff + + success = not expect_fail + return success, js + + def _wait( + self, + target: str, + cmd: str, + match: Union[str, dict], + is_json: bool, + timeout: float, + interval: float, + expect_fail: bool, + flags: int, + ) -> Union[str, dict]: + """Execute a command repeatedly waiting for result until timeout.""" + startt = time.time() + endt = startt + timeout + + success = False + ret = None + while not success and time.time() < endt: + if is_json: + success, ret = self._match_command_json(target, cmd, match, expect_fail) + else: + success, ret = self._match_command( + target, cmd, match, expect_fail, flags + ) + if not success: + time.sleep(interval) + return success, ret + + # --------------------- + # Public APIs for User + # --------------------- + + def include(self, pathname: str, new_section: bool = False): + """See :py:func:`~munet.mutest.userapi.include`. + + :meta private: + """ + path = Path(pathname) + path = self.info.path.parent.joinpath(path) + + self.oplogf( + "include: new path: %s create section: %s currently __in_section: %s", + path, + new_section, + self.__in_section, + ) + + if new_section: + self.oplogf("include: starting new exec section") + self.__start_exec_section(path) + our_info = self.info + # Note we do *not* mark __in_section True + else: + # swap the current path inside the top info + old_path = self.info.path + self.info.path = path + self.oplogf("include: swapped info path: new %s old %s", path, old_path) + + self.__exec_script(path, print_header=new_section, add_newline=new_section) + + if new_section: + # Something within the section creating include has also created a section + # end it, sections do not cross section creating file boundaries + if self.__in_section: + self.oplogf( + "include done: path: %s __in_section calling __end_section", path + ) + self.__end_section() + + # We should now be back to the info we started with, b/c we don't actually + # start a new section (__in_section) that then could have been ended inside + # the included file. + assert our_info == self.info + + self.oplogf( + "include done: path: %s new_section calling __end_section", path + ) + self.__end_section() + else: + # The current top path could be anything due to multiple inline includes as + # well as section swap in and out. Forcibly return the top path to the file + # we are returning to + self.info.path = old_path + self.oplogf("include: restored info path: %s", old_path) + + def __end_section(self): + self.oplogf("__end_section: __in_section: %s", self.__in_section) + info = self.__pop_execinfo() + passed, failed = info.passed, info.failed + self.info.passed += passed + self.info.failed += failed + self.__space_before_result = True + self.oplogf("__end_section setting __in_section to False") + self.__in_section = False + + def __start_exec_section(self, path): + self.oplogf("__start_exec_section: __in_section: %s", self.__in_section) + if self.__in_section: + self.__end_section() + + self.__push_execinfo(path) + self.__space_before_result = False + self.oplogf("NOT setting __in_section to True") + assert not self.__in_section + + def section(self, desc: str): + """See :py:func:`~munet.mutest.userapi.section`. + + :meta private: + """ + self.oplogf("section: __in_section: %s", self.__in_section) + # Grab path before we pop the current info off the top + path = self.info.path + old_steps = self.info.steps + + if self.__in_section: + self.__end_section() + + self.__push_execinfo(path) + add_nl = self.info.steps <= old_steps + + self.__space_before_result = False + self.__in_section = True + self.oplogf(" section setting __in_section to True") + self.__print_header(self.info.tag, desc, add_nl) + + def step(self, target: str, cmd: str) -> str: + """See :py:func:`~munet.mutest.userapi.step`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:STEP:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + ) + return self._command(target, cmd) + + def step_json(self, target: str, cmd: str) -> dict: + """See :py:func:`~munet.mutest.userapi.step_json`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:STEP_JSON:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + ) + return self._command_json(target, cmd) + + def match_step( + self, + target: str, + cmd: str, + match: str, + desc: str = "", + expect_fail: bool = False, + flags: int = re.DOTALL, + ) -> (bool, Union[str, list]): + """See :py:func:`~munet.mutest.userapi.match_step`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:MATCH_STEP:%s:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + desc, + expect_fail, + flags, + ) + success, ret = self._match_command(target, cmd, match, expect_fail, flags) + if desc: + self.__post_result(target, success, desc) + return success, ret + + def test_step(self, expr_or_value: Any, desc: str, target: str = "") -> bool: + """See :py:func:`~munet.mutest.userapi.test`. + + :meta private: + """ + success = bool(expr_or_value) + self.__post_result(target, success, desc) + return success + + def match_step_json( + self, + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + expect_fail: bool = False, + ) -> (bool, Union[str, dict]): + """See :py:func:`~munet.mutest.userapi.match_step_json`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:MATCH_STEP_JSON:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + desc, + expect_fail, + ) + success, ret = self._match_command_json(target, cmd, match, expect_fail) + if desc: + self.__post_result(target, success, desc) + return success, ret + + def wait_step( + self, + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout=10, + interval=0.5, + expect_fail: bool = False, + flags: int = re.DOTALL, + ) -> (bool, Union[str, list]): + """See :py:func:`~munet.mutest.userapi.wait_step`. + + :meta private: + """ + if interval is None: + interval = min(timeout / 20, 0.25) + self.logf( + "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + timeout, + interval, + desc, + expect_fail, + flags, + ) + success, ret = self._wait( + target, cmd, match, False, timeout, interval, expect_fail, flags + ) + if desc: + self.__post_result(target, success, desc) + return success, ret + + def wait_step_json( + self, + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout=10, + interval=None, + expect_fail: bool = False, + ) -> (bool, Union[str, dict]): + """See :py:func:`~munet.mutest.userapi.wait_step_json`. + + :meta private: + """ + if interval is None: + interval = min(timeout / 20, 0.25) + self.logf( + "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + timeout, + interval, + desc, + expect_fail, + ) + success, ret = self._wait( + target, cmd, match, True, timeout, interval, expect_fail, 0 + ) + if desc: + self.__post_result(target, success, desc) + return success, ret + + +# A non-rentrant global to allow for simplified operations +TestCase.g_tc = None + +# pylint: disable=protected-access + + +def _delta_time_str(run_time: float) -> str: + if run_time < 0.0001: + return "0.0" + if run_time < 0.001: + return f"{run_time:1.4f}" + if run_time < 0.01: + return f"{run_time:2.3f}" + if run_time < 0.1: + return f"{run_time:3.2f}" + if run_time < 100: + return f"{run_time:4.1f}" + return f"{run_time:5f}s" + + +def section(desc: str): + """Start a new section for steps, with a description. + + This starts a new section of tests. The result is basically + the same as doing a non-inline include. The current test number + is used to form a new sub-set of test steps. So if the current + test number is 2.3, a section will now number subsequent steps + 2.3.1, 2.3.2, ... + + A subsequent :py:func:`section` or non-inline :py:func:`include` + call ends the current section and advances the base test number. + + Args: + desc: the description for the new section. + """ + TestCase.g_tc.section(desc) + + +def log(fmt, *args, **kwargs): + """Log a message in the testcase output log.""" + return TestCase.g_tc.logf(fmt, *args, **kwargs) + + +def include(pathname: str, new_section=False): + """Include a file as part of testcase. + + Args: + pathname: the file to include. + new_section: if a new section should be created, otherwise + commands are executed inline. + """ + return TestCase.g_tc.include(pathname, new_section) + + +def script_dir() -> Path: + """The pathname to the directory containing the current script file. + + When an include() is called the script_dir is updated to be current with the + includeded file, and is reverted to the previous value when the include completes. + """ + return TestCase.g_tc.info.path.parent + + +def get_target(name: str) -> Commander: + """Get the target object with the given ``name``.""" + return TestCase.g_tc.targets[name] + + +def step(target: str, cmd: str) -> str: + """Execute a ``cmd`` on a ``target`` and return the output. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execute on the target. + + Returns: + Returns the ``str`` output of the ``cmd``. + """ + return TestCase.g_tc.step(target, cmd) + + +def step_json(target: str, cmd: str) -> dict: + """Execute a json ``cmd`` on a ``target`` and return the json object. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execute on the target. + + Returns: + Returns the json object after parsing the ``cmd`` output. + + If json parse fails, a warning is logged and an empty ``dict`` is used. + """ + return TestCase.g_tc.step_json(target, cmd) + + +def test_step(expr_or_value: Any, desc: str, target: str = "") -> bool: + """Evaluates ``expr_or_value`` and posts a result base on it bool(expr). + + If ``expr_or_value`` evaluates to a positive result (i.e., True, non-zero, non-None, + non-empty string, non-empty list, etc..) then a PASS result is recorded, otherwise + record a FAIL is recorded. + + Args: + expr: an expression or value to evaluate + desc: description of this test step. + target: optional target to associate with this test in the result string. + + Returns: + A bool indicating the test PASS or FAIL result. + """ + return TestCase.g_tc.test_step(expr_or_value, desc, target) + + +def match_step( + target: str, + cmd: str, + match: str, + desc: str = "", + expect_fail: bool = False, + flags: int = re.DOTALL, +) -> (bool, Union[str, list]): + """Execute a ``cmd`` on a ``target`` check result. + + Execute ``cmd`` on ``target`` and check if the regexp in ``match`` + matches or doesn't match (according to the ``expect_fail`` value) the + ``cmd`` output. + + If the ``match`` regexp includes groups and if the match succeeds + the group values will be returned in a list, otherwise the command + output is returned. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: regex to match against output. + desc: description of test, if no description then no result is logged. + expect_fail: if True then succeed when the regexp doesn't match. + flags: python regex flags to modify matching behavior + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. + The second value will be a list from ``re.Match.groups()`` if non-empty, + otherwise ``re.Match.group(0)`` if there was a match otherwise None. + """ + return TestCase.g_tc.match_step(target, cmd, match, desc, expect_fail, flags) + + +def match_step_json( + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + expect_fail: bool = False, +) -> (bool, Union[str, dict]): + """Execute a ``cmd`` on a ``target`` check result. + + Execute ``cmd`` on ``target`` and check if the json object in ``match`` + matches or doesn't match (according to the ``expect_fail`` value) the + json output from ``cmd``. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: A json ``str`` or object (``dict``) to compare against the json + output from ``cmd``. + desc: description of test, if no description then no result is logged. + expect_fail: if True then succeed if the a json doesn't match. + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. The + second value is a ``str`` diff if there is a difference found in the json + compare, otherwise the value is the json object (``dict``) from the ``cmd``. + + If json parse fails, a warning is logged and an empty ``dict`` is used. + """ + return TestCase.g_tc.match_step_json(target, cmd, match, desc, expect_fail) + + +def wait_step( + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout: float = 10.0, + interval: float = 0.5, + expect_fail: bool = False, + flags: int = re.DOTALL, +) -> (bool, Union[str, list]): + """Execute a ``cmd`` on a ``target`` repeatedly, looking for a result. + + Execute ``cmd`` on ``target``, every ``interval`` seconds for up to ``timeout`` + seconds until the output of ``cmd`` does or doesn't match (according to the + ``expect_fail`` value) the ``match`` value. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: regexp to match against output. + timeout: The number of seconds to repeat the ``cmd`` looking for a match + (or non-match if ``expect_fail`` is True). + interval: The number of seconds between running the ``cmd``. If not + specified the value is calculated from the timeout value so that on + average the cmd will execute 10 times. The minimum calculated interval + is .25s, shorter values can be passed explicitly. + desc: description of test, if no description then no result is logged. + expect_fail: if True then succeed when the regexp *doesn't* match. + flags: python regex flags to modify matching behavior + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. + The second value will be a list from ``re.Match.groups()`` if non-empty, + otherwise ``re.Match.group(0)`` if there was a match otherwise None. + """ + return TestCase.g_tc.wait_step( + target, cmd, match, desc, timeout, interval, expect_fail, flags + ) + + +def wait_step_json( + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout=10, + interval=None, + expect_fail: bool = False, +) -> (bool, Union[str, dict]): + """Execute a cmd repeatedly and wait for matching result. + + Execute ``cmd`` on ``target``, every ``interval`` seconds until + the output of ``cmd`` matches or doesn't match (according to the + ``expect_fail`` value) ``match``, for up to ``timeout`` seconds. + + ``match`` is a regular expression to search for in the output of ``cmd`` when + ``is_json`` is False. + + When ``is_json`` is True ``match`` must be a json object or a ``str`` which + parses into a json object. Likewise, the ``cmd`` output is parsed into a json + object and then a comparison is done between the two json objects. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: A json object or str representation of one to compare against json + output from ``cmd``. + desc: description of test, if no description then no result is logged. + timeout: The number of seconds to repeat the ``cmd`` looking for a match + (or non-match if ``expect_fail`` is True). + interval: The number of seconds between running the ``cmd``. If not + specified the value is calculated from the timeout value so that on + average the cmd will execute 10 times. The minimum calculated interval + is .25s, shorter values can be passed explicitly. + expect_fail: if True then succeed if the a json doesn't match. + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. + The second value is a ``str`` diff if there is a difference found in the + json compare, otherwise the value is a json object (dict) from the ``cmd`` + output. + + If json parse fails, a warning is logged and an empty ``dict`` is used. + """ + return TestCase.g_tc.wait_step_json( + target, cmd, match, desc, timeout, interval, expect_fail + ) + + +def luInclude(filename, CallOnFail=None): + """Backward compatible API, do not use in new tests.""" + return include(filename) + + +def luLast(usenl=False): + """Backward compatible API, do not use in new tests.""" + del usenl + return TestCase.g_tc.last_m + + +def luCommand( + target, + cmd, + regexp=".", + op="none", + result="", + ltime=10, + returnJson=False, + wait_time=0.5, +): + """Backward compatible API, do not use in new tests. + + Only non-json is verified to any degree of confidence by code inspection. + + For non-json should return match.group() if match else return bool(op == "fail"). + + For json if no diff return the json else diff return bool(op == "jsoncmp_fail") + bug if no json from output (fail parse) could maybe generate diff, which could + then return + """ + if op == "wait": + if returnJson: + return wait_step_json(target, cmd, regexp, result, ltime, wait_time) + + success, _ = wait_step(target, cmd, regexp, result, ltime, wait_time) + match = luLast() + if success and match is not None: + return match.group() + return success + + if op == "none": + if returnJson: + return step_json(target, cmd) + return step(target, cmd) + + if returnJson and op in ("jsoncmp_fail", "jsoncmp_pass"): + expect_fail = op == "jsoncmp_fail" + return match_step_json(target, cmd, regexp, result, expect_fail) + + assert not returnJson + assert op in ("fail", "pass") + expect_fail = op == "fail" + success, _ = match_step(target, cmd, regexp, result, expect_fail) + match = luLast() + if success and match is not None: + return match.group() + return success diff --git a/tests/topotests/munet/mutestshare.py b/tests/topotests/munet/mutestshare.py new file mode 100644 index 0000000000..95ffa74e7b --- /dev/null +++ b/tests/topotests/munet/mutestshare.py @@ -0,0 +1,254 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# January 28 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +"""A tiny init for namespaces in python inspired by the C program tini.""" +import argparse +import errno +import logging +import os +import shlex +import signal +import subprocess +import sys +import threading +import time + +from signal import Signals as S + +from . import linux +from .base import commander + + +child_pid = -1 +very_verbose = False +restore_signals = set() + + +def vdebug(*args, **kwargs): + if very_verbose: + logging.debug(*args, **kwargs) + + +def exit_with_status(pid, status): + try: + ec = status >> 8 if bool(status & 0xFF00) else status | 0x80 + logging.debug("reaped our child, exiting %s", ec) + sys.exit(ec) + except ValueError: + vdebug("pid %s didn't actually exit", pid) + + +def waitpid(tag): + logging.debug("%s: waitid for exiting processes", tag) + idobj = os.waitid(os.P_ALL, 0, os.WEXITED) + pid = idobj.si_pid + status = idobj.si_status + if pid == child_pid: + exit_with_status(pid, status) + else: + logging.debug("%s: reaped zombie pid %s with status %s", tag, pid, status) + + +def new_process_group(): + pid = os.getpid() + try: + pgid = os.getpgrp() + if pgid == pid: + logging.debug("already process group leader %s", pgid) + else: + logging.debug("creating new process group %s", pid) + os.setpgid(pid, 0) + except Exception as error: + logging.warning("unable to get new process group: %s", error) + return + + # Block these in order to allow foregrounding, otherwise we'd get SIGTTOU blocked + signal.signal(S.SIGTTIN, signal.SIG_IGN) + signal.signal(S.SIGTTOU, signal.SIG_IGN) + fd = sys.stdin.fileno() + if not os.isatty(fd): + logging.debug("stdin not a tty no foregrounding required") + else: + try: + # This will error if our session no longer associated with controlling tty. + pgid = os.tcgetpgrp(fd) + if pgid == pid: + logging.debug("process group already in foreground %s", pgid) + else: + logging.debug("making us the foreground pgid backgrounding %s", pgid) + os.tcsetpgrp(fd, pid) + except OSError as error: + if error.errno == errno.ENOTTY: + logging.debug("session is no longer associated with controlling tty") + else: + logging.warning("unable to foreground pgid %s: %s", pid, error) + signal.signal(S.SIGTTIN, signal.SIG_DFL) + signal.signal(S.SIGTTOU, signal.SIG_DFL) + + +def exec_child(exec_args): + # Restore signals to default handling: + for snum in restore_signals: + signal.signal(snum, signal.SIG_DFL) + + # Create new process group. + new_process_group() + + estring = shlex.join(exec_args) + try: + # and exec the process + logging.debug("child: executing '%s'", estring) + os.execvp(exec_args[0], exec_args) + # NOTREACHED + except Exception as error: + logging.warning("child: unable to execute '%s': %s", estring, error) + raise + + +def is_creating_pid_namespace(): + p1name = subprocess.check_output( + "readlink /proc/self/pid", stderr=subprocess.STDOUT, shell=True + ) + p2name = subprocess.check_output( + "readlink /proc/self/pid_for_children", stderr=subprocess.STDOUT, shell=True + ) + return p1name != p2name + + +def restore_namespace(ppid_fd, uflags): + fd = ppid_fd + retry = 3 + for i in range(0, retry): + try: + linux.setns(fd, uflags) + except OSError as error: + logging.warning("could not reset to old namespace fd %s: %s", fd, error) + if i == retry - 1: + raise + time.sleep(1) + os.close(fd) + + +def create_thread_test(): + def runthread(name): + logging.info("In thread: %s", name) + + logging.info("Create thread") + thread = threading.Thread(target=runthread, args=(1,)) + logging.info("Run thread") + thread.start() + logging.info("Join thread") + thread.join() + + +def run(args): + del args + # We look for this b/c the unshare pid will share with /sibn/init + # nselm = "pid_for_children" + # nsflags.append(f"--pid={pp / nselm}") + # mutini now forks when created this way + # cmd.append("--pid") + # cmd.append("--fork") + # cmd.append("--kill-child") + # cmd.append("--mount-proc") + + uflags = linux.CLONE_NEWPID + nslist = ["pid_for_children"] + uflags |= linux.CLONE_NEWNS + nslist.append("mnt") + uflags |= linux.CLONE_NEWNET + nslist.append("net") + + # Before values + pid = os.getpid() + nsdict = {x: os.readlink(f"/tmp/mu-global-proc/{pid}/ns/{x}") for x in nslist} + + # + # UNSHARE + # + create_thread_test() + + ppid = os.getppid() + ppid_fd = linux.pidfd_open(ppid) + linux.unshare(uflags) + + # random syscall's fail until we fork a child to establish the new pid namespace. + global child_pid # pylint: disable=global-statement + child_pid = os.fork() + if not child_pid: + logging.info("In child sleeping") + time.sleep(1200) + sys.exit(1) + + # verify after values differ + nnsdict = {x: os.readlink(f"/tmp/mu-global-proc/{pid}/ns/{x}") for x in nslist} + assert not {k for k in nsdict if nsdict[k] == nnsdict[k]} + + # Remount / and any future mounts below it as private + commander.cmd_raises("mount --make-rprivate /") + # Mount a new /proc in our new namespace + commander.cmd_raises("mount -t proc proc /proc") + + # + # In NEW NS + # + + cid = os.fork() + if not cid: + logging.info("In second child sleeping") + time.sleep(4) + sys.exit(1) + logging.info("Waiting for second child") + os.waitpid(cid, 0) + + try: + create_thread_test() + except Exception as error: + print(error) + + # + # RESTORE + # + + logging.info("In new namespace, restoring old") + # Make sure we can go back, not sure since this is PID namespace, but maybe + restore_namespace(ppid_fd, uflags) + + # verify after values the same + nnsdict = {x: os.readlink(f"/proc/self/ns/{x}") for x in nslist} + assert nsdict == nnsdict + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument( + "-v", dest="verbose", action="count", default=0, help="More -v's, more verbose" + ) + ap.add_argument("rest", nargs=argparse.REMAINDER) + args = ap.parse_args() + + level = logging.DEBUG if args.verbose else logging.INFO + if args.verbose > 1: + global very_verbose # pylint: disable=global-statement + very_verbose = True + logging.basicConfig( + level=level, format="%(asctime)s mutini: %(levelname)s: %(message)s" + ) + + status = 4 + try: + run(args) + except KeyboardInterrupt: + logging.info("exiting (main), received KeyboardInterrupt in main") + except Exception as error: + logging.info("exiting (main), unexpected exception %s", error, exc_info=True) + + sys.exit(status) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/munet/mutini.py b/tests/topotests/munet/mutini.py new file mode 100755 index 0000000000..e5f9931714 --- /dev/null +++ b/tests/topotests/munet/mutini.py @@ -0,0 +1,432 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# January 28 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +"""A tiny init for namespaces in python inspired by the C program tini.""" + + +# pylint: disable=global-statement +import argparse +import errno +import logging +import os +import re +import shlex +import signal +import subprocess +import sys + +from signal import Signals as S + + +try: + from munet import linux +except ModuleNotFoundError: + # We cannot use relative imports and still run this module directly as a script, and + # there are some use cases where we want to run this file as a script. + sys.path.append(os.path.dirname(os.path.realpath(__file__))) + import linux + + +class g: + """Global variables for our program.""" + + child_pid = -1 + orig_pid = os.getpid() + exit_signal = False + pid_status_cache = {} + restore_signals = set() + very_verbose = False + + +unshare_flags = { + "C": linux.CLONE_NEWCGROUP, + "i": linux.CLONE_NEWIPC, + "m": linux.CLONE_NEWNS, + "n": linux.CLONE_NEWNET, + "p": linux.CLONE_NEWPID, + "u": linux.CLONE_NEWUTS, + "T": linux.CLONE_NEWTIME, +} + + +ignored_signals = { + S.SIGTTIN, + S.SIGTTOU, +} +abort_signals = { + S.SIGABRT, + S.SIGBUS, + S.SIGFPE, + S.SIGILL, + S.SIGKILL, + S.SIGSEGV, + S.SIGSTOP, + S.SIGSYS, + S.SIGTRAP, +} +no_prop_signals = abort_signals | ignored_signals | {S.SIGCHLD} + + +def vdebug(*args, **kwargs): + if g.very_verbose: + logging.debug(*args, **kwargs) + + +def get_pid_status_item(status, stat): + m = re.search(rf"(?:^|\n){stat}:\t(.*)(?:\n|$)", status) + return m.group(1).strip() if m else None + + +def pget_pid_status_item(pid, stat): + if pid not in g.pid_status_cache: + with open(f"/proc/{pid}/status", "r", encoding="utf-8") as f: + g.pid_status_cache[pid] = f.read().strip() + return get_pid_status_item(g.pid_status_cache[pid], stat).strip() + + +def get_pid_name(pid): + try: + return get_pid_status_item(g.pid_status_cache[pid], "Name") + except Exception: + return str(pid) + + +# def init_get_child_pids(): +# """Return list of "children" pids. +# We consider any process with a 0 parent pid to also be our child as it +# nsentered our pid namespace from an external parent. +# """ +# g.pid_status_cache.clear() +# pids = (int(x) for x in os.listdir("/proc") if x.isdigit() and x != "1") +# return ( +# x for x in pids if x == g.child_pid or pget_pid_status_item(x, "PPid") == "0" +# ) + + +def exit_with_status(status): + if os.WIFEXITED(status): + ec = os.WEXITSTATUS(status) + elif os.WIFSIGNALED(status): + ec = 0x80 | os.WTERMSIG(status) + else: + ec = 255 + logging.debug("exiting with code %s", ec) + sys.exit(ec) + + +def waitpid(tag): + logging.debug("%s: waitid for exiting process", tag) + idobj = os.waitid(os.P_ALL, 0, os.WEXITED) + pid = idobj.si_pid + status = idobj.si_status + + if pid != g.child_pid: + pidname = get_pid_name(pid) + logging.debug( + "%s: reaped zombie %s (%s) w/ status %s", tag, pid, pidname, status + ) + return + + logging.debug("reaped child with status %s", status) + exit_with_status(status) + # NOTREACHED + + +def sig_trasmit(signum, _): + signame = signal.Signals(signum).name + if g.child_pid == -1: + # We've received a signal after setting up to be init proc + # but prior to fork or fork returning with child pid + logging.debug("received %s prior to child exec, exiting", signame) + sys.exit(0x80 | signum) + + try: + os.kill(g.child_pid, signum) + except OSError as error: + if error.errno != errno.ESRCH: + logging.error( + "error forwarding signal %s to child, exiting: %s", signum, error + ) + sys.exit(0x80 | signum) + logging.debug("child pid %s exited prior to signaling", g.child_pid) + + +def sig_sigchld(signum, _): + assert signum == S.SIGCHLD + try: + waitpid("SIGCHLD") + except ChildProcessError as error: + logging.warning("got SIGCHLD but no pid to wait on: %s", error) + + +def setup_init_signals(): + valid = set(signal.valid_signals()) + named = set(x.value for x in signal.Signals) + for snum in sorted(named): + if snum not in valid: + continue + if S.SIGRTMIN <= snum <= S.SIGRTMAX: + continue + + sname = signal.Signals(snum).name + if snum == S.SIGCHLD: + vdebug("installing local handler for %s", sname) + signal.signal(snum, sig_sigchld) + g.restore_signals.add(snum) + elif snum in ignored_signals: + vdebug("installing ignore handler for %s", sname) + signal.signal(snum, signal.SIG_IGN) + g.restore_signals.add(snum) + elif snum in abort_signals: + vdebug("leaving default handler for %s", sname) + # signal.signal(snum, signal.SIG_DFL) + else: + vdebug("installing trasmit signal handler for %s", sname) + try: + signal.signal(snum, sig_trasmit) + g.restore_signals.add(snum) + except OSError as error: + logging.warning( + "failed installing signal handler for %s: %s", sname, error + ) + + +def new_process_group(): + """Create and lead a new process group. + + This function will create a new process group if we are not yet leading one, and + additionally foreground said process group in our session. This foregrounding + action is copied from tini, and I believe serves a purpose when serving as init + for a container (e.g., podman). + """ + pid = os.getpid() + try: + pgid = os.getpgrp() + if pgid == pid: + logging.debug("already process group leader %s", pgid) + else: + logging.debug("creating new process group %s", pid) + os.setpgid(pid, 0) + except Exception as error: + logging.warning("unable to get new process group: %s", error) + return + + # Block these in order to allow foregrounding, otherwise we'd get SIGTTOU blocked + signal.signal(S.SIGTTIN, signal.SIG_IGN) + signal.signal(S.SIGTTOU, signal.SIG_IGN) + fd = sys.stdin.fileno() + if not os.isatty(fd): + logging.debug("stdin not a tty no foregrounding required") + else: + try: + # This will error if our session no longer associated with controlling tty. + pgid = os.tcgetpgrp(fd) + if pgid == pid: + logging.debug("process group already in foreground %s", pgid) + else: + logging.debug("making us the foreground pgid backgrounding %s", pgid) + os.tcsetpgrp(fd, pid) + except OSError as error: + if error.errno == errno.ENOTTY: + logging.debug("session is no longer associated with controlling tty") + else: + logging.warning("unable to foreground pgid %s: %s", pid, error) + signal.signal(S.SIGTTIN, signal.SIG_DFL) + signal.signal(S.SIGTTOU, signal.SIG_DFL) + + +def is_creating_pid_namespace(): + p1name = subprocess.check_output( + "readlink /proc/self/pid", stderr=subprocess.STDOUT, shell=True + ) + p2name = subprocess.check_output( + "readlink /proc/self/pid_for_children", stderr=subprocess.STDOUT, shell=True + ) + return p1name != p2name + + +def be_init(new_pg, exec_args): + # + # Arrange for us to be killed when our parent dies, this will subsequently also kill + # all procs in any PID namespace we are init for. + # + logging.debug("set us to be SIGKILLed when parent exits") + linux.set_parent_death_signal(signal.SIGKILL) + + # If we are createing a new PID namespace for children... + if g.orig_pid != 1: + logging.debug("started as pid %s", g.orig_pid) + # assert is_creating_pid_namespace() + + # Fork to become pid 1 + logging.debug("forking to become pid 1") + child_pid = os.fork() + if child_pid: + logging.debug("in parent waiting on child pid %s to exit", child_pid) + status = os.wait() + logging.debug("got child exit status %s", status) + exit_with_status(status) + # NOTREACHED + + # We must be pid 1 now. + logging.debug("in child as pid %s", os.getpid()) + assert os.getpid() == 1 + + # We need a new /proc now. + logging.debug("mount new /proc") + linux.mount("proc", "/proc", "proc") + + # If the parent exists kill us using SIGKILL + logging.debug("set us to be SIGKILLed when parent exits") + linux.set_parent_death_signal(signal.SIGKILL) + + if not exec_args: + if not new_pg: + logging.debug("no exec args, no new process group") + # # if 0 == os.getpgid(0): + # status = os.setpgid(0, 1) + # logging.debug("os.setpgid(0, 1) == %s", status) + else: + logging.debug("no exec args, creating new process group") + # No exec so we are the "child". + new_process_group() + + # Reap children as init process + vdebug("installing local handler for SIGCHLD") + signal.signal(signal.SIGCHLD, sig_sigchld) + + while True: + logging.info("init: waiting to reap zombies") + linux.pause() + # NOTREACHED + + # Set (parent) signal handlers before any fork to avoid race + setup_init_signals() + + logging.debug("forking to execute child") + g.child_pid = os.fork() + if g.child_pid == 0: + # In child, restore signals to default handling: + for snum in g.restore_signals: + signal.signal(snum, signal.SIG_DFL) + + # XXX is a new pg right? + new_process_group() + logging.debug("child: executing '%s'", shlex.join(exec_args)) + os.execvp(exec_args[0], exec_args) + # NOTREACHED + + while True: + logging.info("parent: waiting for child pid %s to exit", g.child_pid) + waitpid("parent") + + +def unshare(flags): + """Unshare into new namespaces.""" + uflags = 0 + for flag in flags: + if flag not in unshare_flags: + raise ValueError(f"unknown unshare flag '{flag}'") + uflags |= unshare_flags[flag] + new_pid = bool(uflags & linux.CLONE_NEWPID) + new_mnt = bool(uflags & linux.CLONE_NEWNS) + + logging.debug("unshareing with flags: %s", linux.clone_flag_string(uflags)) + linux.unshare(uflags) + + if new_pid and not new_mnt: + try: + # If we are not creating new mount namspace, remount /proc private + # so that our mount of a new /proc doesn't affect parent namespace + logging.debug("remount /proc recursive private") + linux.mount("none", "/proc", None, linux.MS_REC | linux.MS_PRIVATE) + except OSError as error: + # EINVAL is OK b/c /proc not mounted may cause an error + if error.errno != errno.EINVAL: + raise + if new_mnt: + # Remount root as recursive private. + logging.debug("remount / recursive private") + linux.mount("none", "/", None, linux.MS_REC | linux.MS_PRIVATE) + + # if new_pid: + # logging.debug("mount new /proc") + # linux.mount("proc", "/proc", "proc") + + return new_pid + + +def main(): + # + # Parse CLI args. + # + + ap = argparse.ArgumentParser() + ap.add_argument( + "-P", + "--no-proc-group", + action="store_true", + help="set to inherit the process group", + ) + valid_flags = "".join(unshare_flags) + ap.add_argument( + "--unshare-flags", + help=( + f"string of unshare(1) flags. Supported values from '{valid_flags}'." + " 'm' will remount `/` recursive private. 'p' will remount /proc" + " and fork, and the child will be signaled to exit on exit of parent.." + ), + ) + ap.add_argument( + "-v", dest="verbose", action="count", default=0, help="more -v's, more verbose" + ) + ap.add_argument("rest", nargs=argparse.REMAINDER) + args = ap.parse_args() + + # + # Setup logging. + # + + level = logging.DEBUG if args.verbose else logging.INFO + if args.verbose > 1: + g.very_verbose = True + logging.basicConfig( + level=level, format="%(asctime)s mutini: %(levelname)s: %(message)s" + ) + + # + # Run program + # + + status = 5 + try: + new_pid = False + if args.unshare_flags: + new_pid = unshare(args.unshare_flags) + + if g.orig_pid != 1 and not new_pid: + # Simply hold the namespaces + while True: + logging.info("holding namespace waiting to be signaled to exit") + linux.pause() + # NOTREACHED + + be_init(not args.no_proc_group, args.rest) + # NOTREACHED + logging.critical("Exited from be_init!") + except KeyboardInterrupt: + logging.info("exiting (main), received KeyboardInterrupt in main") + status = 0x80 | signal.SIGINT + except Exception as error: + logging.info("exiting (main), do to exception %s", error, exc_info=True) + + sys.exit(status) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/munet/native.py b/tests/topotests/munet/native.py new file mode 100644 index 0000000000..fecf709d1a --- /dev/null +++ b/tests/topotests/munet/native.py @@ -0,0 +1,2941 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# October 1 2021, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2021-2022, LabN Consulting, L.L.C. +# +# pylint: disable=protected-access +"""A module that defines objects for standalone use.""" +import asyncio +import errno +import getpass +import ipaddress +import logging +import os +import random +import re +import shlex +import socket +import subprocess +import time + +from . import cli +from .base import BaseMunet +from .base import Bridge +from .base import Commander +from .base import LinuxNamespace +from .base import MunetError +from .base import Timeout +from .base import _async_get_exec_path +from .base import _get_exec_path +from .base import cmd_error +from .base import commander +from .base import fsafe_name +from .base import get_exec_path_host +from .config import config_subst +from .config import config_to_dict_with_key +from .config import find_matching_net_config +from .config import find_with_kv +from .config import merge_kind_config + + +class L3ContainerNotRunningError(MunetError): + """Exception if no running container exists.""" + + +def get_loopback_ips(c, nid): + if ip := c.get("ip"): + if ip == "auto": + return [ipaddress.ip_interface("10.255.0.0/32") + nid] + if isinstance(ip, str): + return [ipaddress.ip_interface(ip)] + return [ipaddress.ip_interface(x) for x in ip] + return [] + + +def make_ip_network(net, inc): + n = ipaddress.ip_network(net) + return ipaddress.ip_network( + (n.network_address + inc * n.num_addresses, n.prefixlen) + ) + + +def make_ip_interface(ia, inc): + ia = ipaddress.ip_interface(ia) + # this turns into a /32 fix this + ia = ia + ia.network.num_addresses * inc + # IPv6 + ia = ipaddress.ip_interface(str(ia).replace("/32", "/24").replace("/128", "/64")) + return ia + + +def get_ip_network(c, brid, ipv6=False): + ip = c.get("ipv6" if ipv6 else "ip") + if ip and str(ip) != "auto": + try: + ifip = ipaddress.ip_interface(ip) + if ifip.ip == ifip.network.network_address: + return ifip.network + return ifip + except ValueError: + return ipaddress.ip_network(ip) + if ipv6: + return make_ip_interface("fc00::fe/64", brid) + return make_ip_interface("10.0.0.254/24", brid) + + +def parse_pciaddr(devaddr): + comp = re.match( + "(?:([0-9A-Fa-f]{4}):)?([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}).([0-7])", devaddr + ).groups() + if comp[0] is None: + comp[0] = "0000" + return [int(x, 16) for x in comp] + + +def read_int_value(path): + return int(open(path, encoding="ascii").read()) + + +def read_str_value(path): + return open(path, encoding="ascii").read().strip() + + +def read_sym_basename(path): + return os.path.basename(os.readlink(path)) + + +async def to_thread(func): + """to_thread for python < 3.9.""" + try: + return await asyncio.to_thread(func) + except AttributeError: + logging.warning("Using backport to_thread") + return await asyncio.get_running_loop().run_in_executor(None, func) + + +def convert_ranges_to_bitmask(ranges): + bitmask = 0 + for r in ranges.split(","): + if "-" not in r: + bitmask |= 1 << int(r) + else: + x, y = (int(x) for x in r.split("-")) + for b in range(x, y + 1): + bitmask |= 1 << b + return bitmask + + +class L2Bridge(Bridge): + """A linux bridge with no IP network address.""" + + def __init__(self, name=None, unet=None, logger=None, mtu=None, config=None): + """Create a linux Bridge.""" + super().__init__(name=name, unet=unet, logger=logger, mtu=mtu) + + self.config = config if config else {} + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + await super()._async_delete() + + +class L3Bridge(Bridge): + """A linux bridge with associated IP network address.""" + + def __init__(self, name=None, unet=None, logger=None, mtu=None, config=None): + """Create a linux Bridge.""" + super().__init__(name=name, unet=unet, logger=logger, mtu=mtu) + + self.config = config if config else {} + + self.ip_interface = get_ip_network(self.config, self.id) + if hasattr(self.ip_interface, "network"): + self.ip_address = self.ip_interface.ip + self.ip_network = self.ip_interface.network + self.cmd_raises(f"ip addr add {self.ip_interface} dev {name}") + else: + self.ip_address = None + self.ip_network = self.ip_interface + + self.logger.debug("%s: set IPv4 network address to %s", self, self.ip_interface) + self.cmd_raises("sysctl -w net.ipv4.ip_forward=1") + + self.ip6_interface = None + if self.unet.ipv6_enable: + self.ip6_interface = get_ip_network(self.config, self.id, ipv6=True) + if hasattr(self.ip6_interface, "network"): + self.ip6_address = self.ip6_interface.ip + self.ip6_network = self.ip6_interface.network + self.cmd_raises(f"ip addr add {self.ip6_interface} dev {name}") + else: + self.ip6_address = None + self.ip6_network = self.ip6_interface + + self.logger.debug( + "%s: set IPv6 network address to %s", self, self.ip_interface + ) + self.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1") + + self.is_nat = self.config.get("nat", False) + if self.is_nat: + self.cmd_raises( + "iptables -t nat -A POSTROUTING " + f"-s {self.ip_network} ! -d {self.ip_network} " + f"! -o {self.name} -j MASQUERADE" + ) + + def get_intf_addr(self, ifname, ipv6=False): + # None is a valid interface, we have the same address for all interfaces + # just make sure they aren't asking for something we don't have. + if ifname is not None and ifname not in self.intfs: + return None + return self.ip6_interface if ipv6 else self.ip_interface + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + + if self.config.get("nat", False): + self.cmd_status( + "iptables -t nat -D POSTROUTING " + f"-s {self.ip_network} ! -d {self.ip_network} " + f"! -o {self.name} -j MASQUERADE" + ) + await super()._async_delete() + + +class NodeMixin: + """Node attributes and functionality.""" + + next_ord = 1 + + @classmethod + def _get_next_ord(cls): + # Do not use `cls` here b/c that makes the variable class specific + n = L3NodeMixin.next_ord + L3NodeMixin.next_ord = n + 1 + return n + + def __init__(self, *args, config=None, **kwargs): + """Create a Node.""" + super().__init__(*args, **kwargs) + + self.config = config if config else {} + config = self.config + + self.id = int(config["id"]) if "id" in config else self._get_next_ord() + + self.cmd_p = None + self.container_id = None + self.cleanup_called = False + + # Clear and create rundir early + assert self.unet is not None + self.rundir = self.unet.rundir.joinpath(self.name) + commander.cmd_raises(f"rm -rf {self.rundir}") + commander.cmd_raises(f"mkdir -p {self.rundir}") + + def _shebang_prep(self, config_key): + cmd = self.config.get(config_key, "").strip() + if not cmd: + return [] + + script_name = fsafe_name(config_key) + + # shell_cmd is a union and can be boolean or string + shell_cmd = self.config.get("shell", "/bin/bash") + if not isinstance(shell_cmd, str): + if shell_cmd: + # i.e., "shell: true" + shell_cmd = "/bin/bash" + else: + # i.e., "shell: false" + shell_cmd = "" + + # If we have a shell_cmd then we create a cleanup_cmds file in run_cmd + # and volume mounted it + if shell_cmd: + # Create cleanup cmd file + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmd += "\n" + + # Write out our cleanup cmd file at this time too. + cmdpath = os.path.join(self.rundir, f"{script_name}.shebang") + with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile: + cmdfile.write(f"#!{shell_cmd}\n") + cmdfile.write(cmd) + cmdfile.flush() + commander.cmd_raises(f"chmod 755 {cmdpath}") + + if self.container_id: + # XXX this counts on it being mounted in container, ugly + cmds = [f"/tmp/{script_name}.shebang"] + else: + cmds = [cmdpath] + else: + cmds = [] + if isinstance(cmd, str): + cmds.extend(shlex.split(cmd)) + else: + cmds.extend(cmd) + cmds = [ + x.replace("%CONFIGDIR%", str(self.unet.config_dirname)) for x in cmds + ] + cmds = [x.replace("%RUNDIR%", str(self.rundir)) for x in cmds] + cmds = [x.replace("%NAME%", str(self.name)) for x in cmds] + + return cmds + + async def _async_shebang_cmd(self, config_key, warn=True): + cmds = self._shebang_prep(config_key) + if not cmds: + return 0 + + rc, o, e = await self.async_cmd_status(cmds, warn=warn) + if not rc and warn and (o or e): + self.logger.info( + f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e) + ) + elif rc and warn: + self.logger.warning( + f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e) + ) + else: + self.logger.debug( + f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e) + ) + + return rc + + def has_run_cmd(self) -> bool: + return bool(self.config.get("cmd", "").strip()) + + async def get_proc_child_pid(self, p): + # commander is right for both unshare inline (our proc pidns) + # and non-inline (root pidns). + + # This doesn't work b/c we can't get back to the root pidns + + rootcmd = self.unet.rootcmd + pgrep = rootcmd.get_exec_path("pgrep") + spid = str(p.pid) + for _ in Timeout(4): + if p.returncode is not None: + self.logger.debug("%s: proc %s exited before getting child", self, p) + return None + + rc, o, e = await rootcmd.async_cmd_status( + [pgrep, "-o", "-P", spid], warn=False + ) + if rc == 0: + return int(o.strip()) + + await asyncio.sleep(0.1) + self.logger.debug( + "%s: no child of proc %s: %s", self, p, cmd_error(rc, o, e) + ) + self.logger.warning("%s: timeout getting child pid of proc %s", self, p) + return None + + async def run_cmd(self): + """Run the configured commands for this node.""" + self.logger.debug( + "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir) + ) + + cmds = self._shebang_prep("cmd") + if not cmds: + return + + stdout = open(os.path.join(self.rundir, "cmd.out"), "wb") + stderr = open(os.path.join(self.rundir, "cmd.err"), "wb") + self.cmd_pid = None + self.cmd_p = await self.async_popen( + cmds, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + start_new_session=True, # allows us to signal all children to exit + ) + + # If our process is actually the child of an nsenter fetch its pid. + if self.nsenter_fork: + self.cmd_pid = await self.get_proc_child_pid(self.cmd_p) + + self.logger.debug( + "%s: async_popen %s => %s (cmd_pid %s)", + self, + cmds, + self.cmd_p.pid, + self.cmd_pid, + ) + + self.pytest_hook_run_cmd(stdout, stderr) + + return self.cmd_p + + async def _async_cleanup_cmd(self): + """Run the configured cleanup commands for this node. + + This function is called by subclass' async_cleanup_cmd + """ + self.cleanup_called = True + + return await self._async_shebang_cmd("cleanup-cmd") + + def has_cleanup_cmd(self) -> bool: + return bool(self.config.get("cleanup-cmd", "").strip()) + + async def async_cleanup_cmd(self): + """Run the configured cleanup commands for this node.""" + return await self._async_cleanup_cmd() + + def has_ready_cmd(self) -> bool: + return bool(self.config.get("ready-cmd", "").strip()) + + async def async_ready_cmd(self): + """Run the configured ready commands for this node.""" + return not await self._async_shebang_cmd("ready-cmd", warn=False) + + def cmd_completed(self, future): + self.logger.debug("%s: cmd completed callback", self) + try: + status = future.result() + self.logger.debug( + "%s: node cmd_p completed result: %s cmd: %s", self, status, self.cmd_p + ) + self.cmd_pid = None + self.cmd_p = None + except asyncio.CancelledError: + # Should we stop the container if we have one? + self.logger.debug("%s: node cmd_p.wait() canceled", future) + + def pytest_hook_run_cmd(self, stdout, stderr): + """Handle pytest options related to running the node cmd. + + This function does things such as launch tail'ing windows + on the given files if requested by the user. + + Args: + stdout: file-like object with a ``name`` attribute, or a path to a file. + stderr: file-like object with a ``name`` attribute, or a path to a file. + """ + if not self.unet: + return + + outopt = self.unet.cfgopt.getoption("--stdout") + outopt = outopt if outopt is not None else "" + if outopt == "all" or self.name in outopt.split(","): + outname = stdout.name if hasattr(stdout, "name") else stdout + self.run_in_window(f"tail -F {outname}", title=f"O:{self.name}") + + if stderr: + erropt = self.unet.cfgopt.getoption("--stderr") + erropt = erropt if erropt is not None else "" + if erropt == "all" or self.name in erropt.split(","): + errname = stderr.name if hasattr(stderr, "name") else stderr + self.run_in_window(f"tail -F {errname}", title=f"E:{self.name}") + + def pytest_hook_open_shell(self): + if not self.unet: + return + + gdbcmd = self.config.get("gdb-cmd") + shellopt = self.unet.cfgopt.getoption("--gdb", "") + should_gdb = gdbcmd and (shellopt == "all" or self.name in shellopt.split(",")) + use_emacs = self.unet.cfgopt.getoption("--gdb-use-emacs", False) + + if should_gdb and not use_emacs: + cmds = self.config.get("gdb-target-cmds", []) + for cmd in cmds: + gdbcmd += f" '-ex={cmd}'" + + bps = self.unet.cfgopt.getoption("--gdb-breakpoints", "").split(",") + for bp in bps: + gdbcmd += f" '-ex=b {bp}'" + + cmds = self.config.get("gdb-run-cmd", []) + for cmd in cmds: + gdbcmd += f" '-ex={cmd}'" + + self.run_in_window(gdbcmd) + elif should_gdb and use_emacs: + gdbcmd = gdbcmd.replace("gdb ", "gdb -i=mi ") + ecbin = self.get_exec_path("emacsclient") + # output = self.cmd_raises( + # [ecbin, "--eval", f"(gdb \"{gdbcmd} -ex='p 123456'\")"] + # ) + _ = self.cmd_raises([ecbin, "--eval", f'(gdb "{gdbcmd}")']) + + # can't figure out how to wait until symbols are loaded, until we do we just + # have to wait "long enough" for the symbol load to finish :/ + # for _ in range(100): + # output = self.cmd_raises( + # [ + # ecbin, + # "--eval", + # f"gdb-first-prompt", + # ] + # ) + # if output == "nil\n": + # break + # time.sleep(0.25) + + time.sleep(10) + + cmds = self.config.get("gdb-target-cmds", []) + for cmd in cmds: + # we may want to quote quotes in the cmd string + self.cmd_raises( + [ + ecbin, + "--eval", + f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")', + ] + ) + + bps = self.unet.cfgopt.getoption("--gdb-breakpoints", "").split(",") + for bp in bps: + cmd = f"br {bp}" + self.cmd_raises( + [ + ecbin, + "--eval", + f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")', + ] + ) + + cmds = self.config.get("gdb-run-cmds", []) + for cmd in cmds: + # we may want to quote quotes in the cmd string + self.cmd_raises( + [ + ecbin, + "--eval", + f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")', + ] + ) + gdbcmd += f" '-ex={cmd}'" + + shellopt = self.unet.cfgopt.getoption("--shell") + shellopt = shellopt if shellopt else "" + if shellopt == "all" or self.name in shellopt.split(","): + self.run_in_window("bash") + + async def _async_delete(self): + self.logger.debug("%s: NodeMixin sub-class _async_delete", self) + + if self.cmd_p: + await self.async_cleanup_proc(self.cmd_p, self.cmd_pid) + self.cmd_p = None + + # Next call users "cleanup_cmd:" + try: + if not self.cleanup_called: + await self.async_cleanup_cmd() + except Exception as error: + self.logger.warning( + "Got an error during delete from async_cleanup_cmd: %s", error + ) + + # delete the LinuxNamespace/InterfaceMixin + await super()._async_delete() + + +class SSHRemote(NodeMixin, Commander): + """SSHRemote a node representing an ssh connection to something.""" + + def __init__( + self, + name, + server, + port=22, + user=None, + password=None, + idfile=None, + **kwargs, + ): + super().__init__(name, **kwargs) + + self.logger.debug("%s: creating", self) + + # Things done in LinuxNamepsace we need to replicate here. + self.rundir = self.unet.rundir.joinpath(self.name) + self.unet.cmd_raises(f"rm -rf {self.rundir}") + self.unet.cmd_raises(f"mkdir -p {self.rundir}") + + self.mgmt_ip = None + self.mgmt_ip6 = None + + self.port = port + + if user: + self.user = user + elif "SUDO_USER" in os.environ: + self.user = os.environ["SUDO_USER"] + else: + self.user = getpass.getuser() + self.password = password + self.idfile = idfile + + self.server = f"{self.user}@{server}" + + # Setup our base `pre-cmd` values + # + # We maybe should add environment variable transfer here in particular + # MUNET_NODENAME. The problem is the user has to explicitly approve + # of SendEnv variables. + self.__base_cmd = [ + get_exec_path_host("sudo"), + "-E", + f"-u{self.user}", + get_exec_path_host("ssh"), + ] + if port != 22: + self.__base_cmd.append(f"-p{port}") + self.__base_cmd.append("-q") + self.__base_cmd.append("-oStrictHostKeyChecking=no") + self.__base_cmd.append("-oUserKnownHostsFile=/dev/null") + if self.idfile: + self.__base_cmd.append(f"-i{self.idfile}") + # Would be nice but has to be accepted by server config so not very useful. + # self.__base_cmd.append("-oSendVar='TEST'") + self.__base_cmd_pty = list(self.__base_cmd) + self.__base_cmd_pty.append("-t") + self.__base_cmd.append(self.server) + self.__base_cmd_pty.append(self.server) + # self.set_pre_cmd(pre_cmd, pre_cmd_tty) + + self.logger.info("%s: created", self) + + def has_ready_cmd(self) -> bool: + return bool(self.config.get("ready-cmd", "").strip()) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, **kwargs): + pre_cmd = [] + if self.unet: + pre_cmd = self.unet._get_pre_cmd(False, use_pty, ns_only=False, **kwargs) + if ns_only: + return pre_cmd + + # XXX grab the env from kwargs and add to podman exec + # env = kwargs.get("env", {}) + if use_pty: + pre_cmd = pre_cmd + self.__base_cmd_pty + else: + pre_cmd = pre_cmd + self.__base_cmd + return shlex.join(pre_cmd) if use_str else list(pre_cmd) + + def _get_cmd_as_list(self, cmd): + """Given a list or string return a list form for execution. + + If cmd is a string then [cmd] is returned, for most other + node types ["bash", "-c", cmd] is returned but in our case + ssh is the shell. + + Args: + cmd: list or string representing the command to execute. + str_shell: if True and `cmd` is a string then run the + command using bash -c + Returns: + list of commands to execute. + """ + return [cmd] if isinstance(cmd, str) else cmd + + +# Would maybe like to refactor this into L3 and Node +class L3NodeMixin(NodeMixin): + """A linux namespace with IP attributes.""" + + def __init__(self, *args, unet=None, **kwargs): + """Create an L3Node.""" + # logging.warning( + # "L3NodeMixin: config %s unet %s kwargs %s", config, unet, kwargs + # ) + super().__init__(*args, unet=unet, **kwargs) + + self.mgmt_ip = None # set in parser.py + self.mgmt_ip6 = None # set in parser.py + self.host_intfs = {} + self.phy_intfs = {} + self.phycount = 0 + self.phy_odrivers = {} + self.tapmacs = {} + + self.intf_tc_count = 0 + + # super().__init__(name=name, **kwargs) + + self.mount_volumes() + + # ----------------------- + # Setup node's networking + # ----------------------- + if not unet.ipv6_enable: + # Disable IPv6 + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=0") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=1") + else: + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=1") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=0") + + self.next_p2p_network = ipaddress.ip_network(f"10.254.{self.id}.0/31") + self.next_p2p_network6 = ipaddress.ip_network(f"fcff:ffff:{self.id:02x}::/127") + + self.loopback_ip = None + self.loopback_ips = get_loopback_ips(self.config, self.id) + self.loopback_ip = self.loopback_ips[0] if self.loopback_ips else None + if self.loopback_ip: + self.cmd_raises_nsonly(f"ip addr add {self.loopback_ip} dev lo") + self.cmd_raises_nsonly("ip link set lo up") + for i, ip in enumerate(self.loopback_ips[1:]): + self.cmd_raises_nsonly(f"ip addr add {ip} dev lo:{i}") + + # ------------------- + # Setup node's rundir + # ------------------- + + # Not host path based, but we assume same + self.set_ns_cwd(self.rundir) + + # Save the namespace pid + with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f: + f.write(f"{self.pid}\n") + + with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f: + f.write(f'{" ".join([str(x) for x in self.pids])}\n') + + # Create a hosts file to map our name + hosts_file = os.path.join(self.rundir, "hosts.txt") + with open(hosts_file, "w", encoding="ascii") as hf: + hf.write( + f"""127.0.0.1\tlocalhost {self.name} +::1\tip6-localhost ip6-loopback +fe00::0\tip6-localnet +ff00::0\tip6-mcastprefix +ff02::1\tip6-allnodes +ff02::2\tip6-allrouters +""" + ) + if hasattr(self, "bind_mount"): + self.bind_mount(hosts_file, "/etc/hosts") + + async def console( + self, + concmd, + prompt=r"(^|\r?\n)[^#\$]*[#\$] ", + is_bourne=True, + user=None, + password=None, + expects=None, + sends=None, + use_pty=False, + will_echo=False, + logfile_prefix="console", + trace=True, + **kwargs, + ): + """Create a REPL (read-eval-print-loop) driving a console. + + Args: + concmd: string or list to popen with, or an already open socket + prompt: the REPL prompt to look for, the function returns when seen + is_bourne: True if the console is a bourne shell + user: user name to log in with + password: password to log in with + expects: a list of regex other than the prompt, the standard user, or + password to look for. "ogin:" or "[Pp]assword:"r. + sends: what to send when an element of `expects` matches. Can be the + empty string to send nothing. + use_pty: true for pty based expect, otherwise uses popen (pipes/files) + will_echo: bash is buggy in that it echo's to non-tty unlike any other + sh/ksh, set this value to true if running back + logfile_prefix: prefix for 3 logfiles opened to track the console i/o + trace: trace the send/expect sequence + **kwargs: kwargs passed on the _spawn. + """ + lfname = os.path.join(self.rundir, f"{logfile_prefix}-log.txt") + logfile = open(lfname, "a+", encoding="utf-8") + logfile.write("-- start logging for: '{}' --\n".format(concmd)) + + lfname = os.path.join(self.rundir, f"{logfile_prefix}-read-log.txt") + logfile_read = open(lfname, "a+", encoding="utf-8") + logfile_read.write("-- start read logging for: '{}' --\n".format(concmd)) + + lfname = os.path.join(self.rundir, f"{logfile_prefix}-send-log.txt") + logfile_send = open(lfname, "a+", encoding="utf-8") + logfile_send.write("-- start send logging for: '{}' --\n".format(concmd)) + + expects = [] if expects is None else expects + sends = [] if sends is None else sends + if user: + expects.append("ogin:") + sends.append(user + "\n") + if password is not None: + expects.append("assword:") + sends.append(password + "\n") + repl = await self.shell_spawn( + concmd, + prompt, + expects=expects, + sends=sends, + use_pty=use_pty, + will_echo=will_echo, + is_bourne=is_bourne, + logfile=logfile, + logfile_read=logfile_read, + logfile_send=logfile_send, + trace=trace, + **kwargs, + ) + return repl + + async def monitor( + self, + sockpath, + prompt=r"\(qemu\) ", + ): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(sockpath) + + pfx = os.path.basename(sockpath) + + lfname = os.path.join(self.rundir, f"{pfx}-log.txt") + logfile = open(lfname, "a+", encoding="utf-8") + logfile.write("-- start logging for: '{}' --\n".format(sock)) + + lfname = os.path.join(self.rundir, f"{pfx}-read-log.txt") + logfile_read = open(lfname, "a+", encoding="utf-8") + logfile_read.write("-- start read logging for: '{}' --\n".format(sock)) + + p = self.spawn(sock, prompt, logfile=logfile, logfile_read=logfile_read) + from .base import ShellWrapper # pylint: disable=C0415 + + p.send("\n") + return ShellWrapper(p, prompt, None, will_echo=True, escape_ansi=True) + + def mount_volumes(self): + for m in self.config.get("volumes", []): + if isinstance(m, str): + s = m.split(":", 1) + if len(s) == 1: + self.tmpfs_mount(s[0]) + else: + spath = s[0] + if spath[0] == ".": + spath = os.path.abspath( + os.path.join(self.unet.config_dirname, spath) + ) + self.bind_mount(spath, s[1]) + continue + raise NotImplementedError("complex mounts for non-containers") + + def get_ifname(self, netname): + for c in self.config["connections"]: + if c["to"] == netname: + return c["name"] + return None + + def set_lan_addr(self, switch, cconf): + if ip := cconf.get("ip"): + ipaddr = ipaddress.ip_interface(ip) + assert ipaddr.version == 4 + elif self.unet.autonumber and "ip" not in cconf: + self.logger.debug( + "%s: prefixlen of switch %s is %s", + self, + switch.name, + switch.ip_network.prefixlen, + ) + n = switch.ip_network + ipaddr = ipaddress.ip_interface((n.network_address + self.id, n.prefixlen)) + else: + ipaddr = None + + if ip := cconf.get("ipv6"): + ip6addr = ipaddress.ip_interface(ip) + assert ipaddr.version == 6 + elif self.unet.ipv6_enable and self.unet.autonumber and "ipv6" not in cconf: + self.logger.debug( + "%s: prefixlen of switch %s is %s", + self, + switch.name, + switch.ip6_network.prefixlen, + ) + n = switch.ip6_network + ip6addr = ipaddress.ip_interface((n.network_address + self.id, n.prefixlen)) + else: + ip6addr = None + + dns_network = self.unet.topoconf.get("dns-network") + for ip in (ipaddr, ip6addr): + if not ip: + continue + ipcmd = "ip " if ip.version == 4 else "ip -6 " + if dns_network and dns_network == switch.name: + if ip.version == 4: + self.mgmt_ip = ip.ip + else: + self.mgmt_ip6 = ip.ip + ifname = cconf["name"] + self.set_intf_addr(ifname, ip) + self.logger.debug("%s: adding %s to lan intf %s", self, ip, ifname) + if not self.is_vm: + self.intf_ip_cmd(ifname, ipcmd + f"addr add {ip} dev {ifname}") + if hasattr(switch, "is_nat") and switch.is_nat: + swaddr = ( + switch.ip_address if ip.version == 4 else switch.ip6_address + ) + self.cmd_raises(ipcmd + f"route add default via {swaddr}") + + def _set_p2p_addr(self, other, cconf, occonf, ipv6=False): + ipkey = "ipv6" if ipv6 else "ip" + ipaddr = ipaddress.ip_interface(cconf[ipkey]) if cconf.get(ipkey) else None + oipaddr = ipaddress.ip_interface(occonf[ipkey]) if occonf.get(ipkey) else None + self.logger.debug( + "%s: set_p2p_addr %s %s %s", self, other.name, ipaddr, oipaddr + ) + + if not ipaddr and not oipaddr: + if self.unet.autonumber: + if ipv6: + n = self.next_p2p_network6 + self.next_p2p_network6 = make_ip_network(n, 1) + else: + n = self.next_p2p_network + self.next_p2p_network = make_ip_network(n, 1) + + ipaddr = ipaddress.ip_interface(n) + oipaddr = ipaddress.ip_interface((ipaddr.ip + 1, n.prefixlen)) + else: + return + + if ipaddr: + ifname = cconf["name"] + self.set_intf_addr(ifname, ipaddr) + self.logger.debug("%s: adding %s to p2p intf %s", self, ipaddr, ifname) + if "physical" not in cconf and not self.is_vm: + self.intf_ip_cmd(ifname, f"ip addr add {ipaddr} dev {ifname}") + + if oipaddr: + oifname = occonf["name"] + other.set_intf_addr(oifname, oipaddr) + self.logger.debug( + "%s: adding %s to other p2p intf %s", other, oipaddr, oifname + ) + if "physical" not in occonf and not other.is_vm: + other.intf_ip_cmd(oifname, f"ip addr add {oipaddr} dev {oifname}") + + def set_p2p_addr(self, other, cconf, occonf): + self._set_p2p_addr(other, cconf, occonf, ipv6=False) + if self.unet.ipv6_enable: + self._set_p2p_addr(other, cconf, occonf, ipv6=True) + + async def add_host_intf(self, hname, lname, mtu=None): + if hname in self.host_intfs: + return + self.host_intfs[hname] = lname + self.unet.rootcmd.cmd_nostatus(f"ip link set {hname} down ") + self.unet.rootcmd.cmd_raises(f"ip link set {hname} netns {self.pid}") + self.cmd_raises(f"ip link set {hname} name {lname}") + if mtu: + self.cmd_raises(f"ip link set {lname} mtu {mtu}") + self.cmd_raises(f"ip link set {lname} up") + + async def rem_host_intf(self, hname): + lname = self.host_intfs[hname] + self.cmd_raises(f"ip link set {lname} down") + self.cmd_raises(f"ip link set {lname} name {hname}") + self.cmd_raises(f"ip link set {hname} netns 1") + del self.host_intfs[hname] + + async def add_phy_intf(self, devaddr, lname): + """Add a physical inteface (i.e. mv it to vfio-pci driver. + + This is primarily useful for Qemu, but also for things like TREX or DPDK + """ + if devaddr in self.phy_intfs: + return + self.phy_intfs[devaddr] = lname + index = len(self.phy_intfs) + + _, _, off, fun = parse_pciaddr(devaddr) + doffset = off * 8 + fun + + is_virtual = self.unet.rootcmd.path_exists( + f"/sys/bus/pci/devices/{devaddr}/physfn" + ) + if is_virtual: + pfname = self.unet.rootcmd.cmd_raises( + f"ls -1 /sys/bus/pci/devices/{devaddr}/physfn/net" + ).strip() + pdevaddr = read_sym_basename(f"/sys/bus/pci/devices/{devaddr}/physfn") + _, _, poff, pfun = parse_pciaddr(pdevaddr) + poffset = poff * 8 + pfun + + offset = read_int_value( + f"/sys/bus/pci/devices/{devaddr}/physfn/sriov_offset" + ) + stride = read_int_value( + f"/sys/bus/pci/devices/{devaddr}/physfn/sriov_stride" + ) + vf = (doffset - offset - poffset) // stride + mac = f"02:cc:cc:cc:{index:02x}:{self.id:02x}" + # Some devices require the parent to be up (e.g., ixbge) + self.unet.rootcmd.cmd_raises(f"ip link set {pfname} up") + self.unet.rootcmd.cmd_raises(f"ip link set {pfname} vf {vf} mac {mac}") + self.unet.rootcmd.cmd_status(f"ip link set {pfname} vf {vf} trust on") + self.tapmacs[devaddr] = mac + + self.logger.info("Adding physical PCI device %s as %s", devaddr, lname) + + # Get interface name and set to down if present + ec, ifname, _ = self.unet.rootcmd.cmd_status( + f"ls /sys/bus/pci/devices/{devaddr}/net/", warn=False + ) + ifname = ifname.strip() + if not ec and ifname: + # XXX Should only do this is the device is up, and then likewise return it + # up on exit self.phy_intfs_hostname[devaddr] = ifname + self.logger.info( + "Setting physical PCI device %s named %s down", devaddr, ifname + ) + self.unet.rootcmd.cmd_status( + f"ip link set {ifname} down 2> /dev/null || true" + ) + + # Get the current bound driver, and unbind + try: + driver = read_sym_basename(f"/sys/bus/pci/devices/{devaddr}/driver") + driver = driver.strip() + except Exception: + driver = "" + if driver: + if driver == "vfio-pci": + self.logger.info( + "Physical PCI device %s already bound to vfio-pci", devaddr + ) + return + self.logger.info( + "Unbinding physical PCI device %s from driver %s", devaddr, driver + ) + self.phy_odrivers[devaddr] = driver + self.unet.rootcmd.cmd_raises( + f"echo {devaddr} > /sys/bus/pci/drivers/{driver}/unbind" + ) + + # Add the device vendor and device id to vfio-pci in case it's the first time + vendor = read_str_value(f"/sys/bus/pci/devices/{devaddr}/vendor") + devid = read_str_value(f"/sys/bus/pci/devices/{devaddr}/device") + self.logger.info("Adding device IDs %s:%s to vfio-pci", vendor, devid) + ec, _, _ = self.unet.rootcmd.cmd_status( + f"echo {vendor} {devid} > /sys/bus/pci/drivers/vfio-pci/new_id", warn=False + ) + + if not self.unet.rootcmd.path_exists(f"/sys/bus/pci/driver/vfio-pci/{devaddr}"): + # Bind to vfio-pci if wasn't added with new_id + self.logger.info("Binding physical PCI device %s to vfio-pci", devaddr) + ec, _, _ = self.unet.rootcmd.cmd_status( + f"echo {devaddr} > /sys/bus/pci/drivers/vfio-pci/bind" + ) + + async def rem_phy_intf(self, devaddr): + """Remove a physical inteface (i.e. mv it away from vfio-pci driver. + + This is primarily useful for Qemu, but also for things like TREX or DPDK + """ + lname = self.phy_intfs.get(devaddr, "") + if lname: + del self.phy_intfs[devaddr] + + # ifname = self.phy_intfs_hostname.get(devaddr, "") + # if ifname + # del self.phy_intfs_hostname[devaddr] + + driver = self.phy_odrivers.get(devaddr, "") + if not driver: + self.logger.info( + "Physical PCI device %s was bound to vfio-pci on entry", devaddr + ) + return + + self.logger.info( + "Unbinding physical PCI device %s from driver vfio-pci", devaddr + ) + self.unet.rootcmd.cmd_status( + f"echo {devaddr} > /sys/bus/pci/drivers/vfio-pci/unbind" + ) + + self.logger.info("Binding physical PCI device %s to driver %s", devaddr, driver) + ec, _, _ = self.unet.rootcmd.cmd_status( + f"echo {devaddr} > /sys/bus/pci/drivers/{driver}/bind" + ) + if not ec: + del self.phy_odrivers[devaddr] + + async def _async_delete(self): + self.logger.debug("%s: L3NodeMixin sub-class _async_delete", self) + + # XXX do we need to run the cleanup command before these infra changes? + + # remove any hostintf interfaces + for hname in list(self.host_intfs): + await self.rem_host_intf(hname) + + # remove any hostintf interfaces + for devaddr in list(self.phy_intfs): + await self.rem_phy_intf(devaddr) + + # delete the LinuxNamespace/InterfaceMixin + await super()._async_delete() + + +class L3NamespaceNode(L3NodeMixin, LinuxNamespace): + """A namespace L3 node.""" + + def __init__(self, name, pid=True, **kwargs): + # logging.warning( + # "L3NamespaceNode: name %s MRO: %s kwargs %s", + # name, + # L3NamespaceNode.mro(), + # kwargs, + # ) + super().__init__(name, pid=pid, **kwargs) + super().pytest_hook_open_shell() + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + await super()._async_delete() + + +class L3ContainerNode(L3NodeMixin, LinuxNamespace): + """An container (podman) based L3 node.""" + + def __init__(self, name, config, **kwargs): + """Create a Container Node.""" + self.cont_exec_paths = {} + self.container_id = None + self.container_image = config["image"] + self.extra_mounts = [] + assert self.container_image + + self.cmd_p = None + self.__base_cmd = [] + self.__base_cmd_pty = [] + + # don't we have a mutini or cat process? + super().__init__( + name=name, + config=config, + # pid=True, + # cgroup=True, + # private_mounts=["/sys/fs/cgroup:/sys/fs/cgroup"], + **kwargs, + ) + + @property + def is_container(self): + return True + + def get_exec_path(self, binary): + """Return the full path to the binary executable inside the image. + + `binary` :: binary name or list of binary names + """ + return _get_exec_path(binary, self.cmd_status, self.cont_exec_paths) + + async def async_get_exec_path(self, binary): + """Return the full path to the binary executable inside the image. + + `binary` :: binary name or list of binary names + """ + path = await _async_get_exec_path( + binary, self.async_cmd_status, self.cont_exec_paths + ) + return path + + def get_exec_path_host(self, binary): + """Return the full path to the binary executable on the host. + + `binary` :: binary name or list of binary names + """ + return get_exec_path_host(binary) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + if ns_only: + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + if not self.cmd_p: + if self.container_id: + s = f"{self}: Running command in namespace b/c container exited" + self.logger.warning("%s", s) + raise L3ContainerNotRunningError(s) + self.logger.debug("%s: Running command in namespace b/c no container", self) + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + # We need to enter our namespaces when running the podman command + pre_cmd = super()._get_pre_cmd( + False, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + # XXX grab the env from kwargs and add to podman exec + # env = kwargs.get("env", {}) + if use_pty: + pre_cmd = pre_cmd + self.__base_cmd_pty + else: + pre_cmd = pre_cmd + self.__base_cmd + return shlex.join(pre_cmd) if use_str else pre_cmd + + def tmpfs_mount(self, inner): + # eventually would be nice to support live mounting + assert not self.container_id + self.logger.debug("Mounting tmpfs on %s", inner) + self.extra_mounts.append(f"--mount=type=tmpfs,destination={inner}") + + def bind_mount(self, outer, inner): + # eventually would be nice to support live mounting + assert not self.container_id + # First bind the mount in the parent this allows things like /etc/hosts to work + # correctly when running "nsonly" commands + super().bind_mount(outer, inner) + # Then arrange for binding in the container as well. + self.logger.debug("Bind mounting %s on %s", outer, inner) + if not self.test_nsonly("-e", outer): + self.cmd_raises_nsonly(f"mkdir -p {outer}") + self.extra_mounts.append(f"--mount=type=bind,src={outer},dst={inner}") + + def mount_volumes(self): + args = [] + for m in self.config.get("volumes", []): + if isinstance(m, str): + s = m.split(":", 1) + if len(s) == 1: + args.append("--mount=type=tmpfs,destination=" + m) + else: + spath = s[0] + spath = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), spath + ) + ) + if not self.test_nsonly("-e", spath): + self.cmd_raises_nsonly(f"mkdir -p {spath}") + args.append(f"--mount=type=bind,src={spath},dst={s[1]}") + continue + + for m in self.config.get("mounts", []): + margs = ["type=" + m["type"]] + for k, v in m.items(): + if k == "type": + continue + if v: + if k in ("src", "source"): + v = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), v + ) + ) + if not self.test_nsonly("-e", v): + self.cmd_raises_nsonly(f"mkdir -p {v}") + margs.append(f"{k}={v}") + else: + margs.append(f"{k}") + args.append("--mount=" + ",".join(margs)) + + if args: + # Need to work on a way to mount into live container too + self.extra_mounts += args + + def has_run_cmd(self) -> bool: + return True + + async def run_cmd(self): + """Run the configured commands for this node.""" + self.logger.debug("%s: starting container", self.name) + self.logger.debug( + "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir) + ) + + self.container_id = f"{self.name}-{os.getpid()}" + proc_path = self.unet.proc_path if self.unet else "/proc" + cmds = [ + get_exec_path_host("podman"), + "run", + f"--name={self.container_id}", + # f"--net=ns:/proc/{self.pid}/ns/net", + f"--net=ns:{proc_path}/{self.pid}/ns/net", + f"--hostname={self.name}", + f"--add-host={self.name}:127.0.0.1", + # We can't use --rm here b/c podman fails on "stop". + # u"--rm", + ] + + if self.config.get("init", True): + cmds.append("--init") + + if self.config.get("privileged", False): + cmds.append("--privileged") + # If we don't do this then the host file system is remounted read-only on + # exit! + cmds.append("--systemd=false") + else: + cmds.extend( + [ + # "--cap-add=SYS_ADMIN", + "--cap-add=NET_ADMIN", + "--cap-add=NET_RAW", + ] + ) + + # Add volumes: + if self.extra_mounts: + cmds += self.extra_mounts + + # Add environment variables: + envdict = self.config.get("env", {}) + if envdict is None: + envdict = {} + for k, v in envdict.items(): + cmds.append(f"--env={k}={v}") + + # Update capabilities + cmds += [f"--cap-add={x}" for x in self.config.get("cap-add", [])] + cmds += [f"--cap-drop={x}" for x in self.config.get("cap-drop", [])] + # cmds += [f"--expose={x.split(':')[0]}" for x in self.config.get("ports", [])] + cmds += [f"--publish={x}" for x in self.config.get("ports", [])] + + # Add extra flags from user: + if "podman" in self.config: + for x in self.config["podman"].get("extra-args", []): + cmds.append(x.strip()) + + # shell_cmd is a union and can be boolean or string + shell_cmd = self.config.get("shell", "/bin/bash") + if not isinstance(shell_cmd, str): + if shell_cmd: + shell_cmd = "/bin/bash" + else: + shell_cmd = "" + + # Create shebang files, filled later on + for key in ("cleanup-cmd", "ready-cmd"): + shebang_cmd = self.config.get(key, "").strip() + if shell_cmd and shebang_cmd: + script_name = fsafe_name(key) + # Will write the file contents out when the command is run + shebang_cmdpath = os.path.join(self.rundir, f"{script_name}.shebang") + await self.async_cmd_raises_nsonly(f"touch {shebang_cmdpath}") + await self.async_cmd_raises_nsonly(f"chmod 755 {shebang_cmdpath}") + cmds += [ + # How can we override this? + # u'--entrypoint=""', + f"--volume={shebang_cmdpath}:/tmp/{script_name}.shebang", + ] + + cmd = self.config.get("cmd", "").strip() + + # See if we have a custom update for this `kind` + if kind := self.config.get("kind", None): + if kind in kind_run_cmd_update: + cmds, cmd = await kind_run_cmd_update[kind](self, shell_cmd, cmds, cmd) + + # Create running command file + if shell_cmd and cmd: + assert isinstance(cmd, str) + # make cmd \n terminated for script + cmd = cmd.rstrip() + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmd += "\n" + cmdpath = os.path.join(self.rundir, "cmd.shebang") + with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile: + cmdfile.write(f"#!{shell_cmd}\n") + cmdfile.write(cmd) + cmdfile.flush() + self.cmd_raises_nsonly(f"chmod 755 {cmdpath}") + cmds += [ + # How can we override this? + # u'--entrypoint=""', + f"--volume={cmdpath}:/tmp/cmds.shebang", + self.container_image, + "/tmp/cmds.shebang", + ] + else: + # `cmd` is a direct run (no shell) cmd + cmds.append(self.container_image) + if cmd: + if isinstance(cmd, str): + cmds.extend(shlex.split(cmd)) + else: + cmds.extend(cmd) + + cmds = [ + x.replace("%CONFIGDIR%", str(self.unet.config_dirname)) for x in cmds + ] + cmds = [x.replace("%RUNDIR%", str(self.rundir)) for x in cmds] + cmds = [x.replace("%NAME%", str(self.name)) for x in cmds] + + stdout = open(os.path.join(self.rundir, "cmd.out"), "wb") + stderr = open(os.path.join(self.rundir, "cmd.err"), "wb") + # Using nsonly avoids using `podman exec` to execute the cmds. + self.cmd_p = await self.async_popen_nsonly( + cmds, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + start_new_session=True, # keeps main tty signals away from podman + ) + + self.logger.debug("%s: async_popen => %s", self, self.cmd_p.pid) + + self.pytest_hook_run_cmd(stdout, stderr) + + # --------------------------------------- + # Now let's wait until container shows up + # --------------------------------------- + timeout = Timeout(30) + while self.cmd_p.returncode is None and not timeout.is_expired(): + o = await self.async_cmd_raises_nsonly( + f"podman ps -q -f name={self.container_id}" + ) + if o.strip(): + break + elapsed = int(timeout.elapsed()) + if elapsed <= 3: + await asyncio.sleep(0.1) + else: + self.logger.info("%s: run_cmd taking more than %ss", self, elapsed) + await asyncio.sleep(1) + if self.cmd_p.returncode is not None: + # leave self.container_id set to cause exception on use + self.logger.warning( + "%s: run_cmd exited quickly (%ss) rc: %s", + self, + timeout.elapsed(), + self.cmd_p.returncode, + ) + elif timeout.is_expired(): + self.logger.critical( + "%s: timeout (%ss) waiting for container to start", + self.name, + timeout.elapsed(), + ) + assert not timeout.is_expired() + + # + # Set our precmd for executing in the container + # + self.__base_cmd = [ + get_exec_path_host("podman"), + "exec", + f"-eMUNET_RUNDIR={self.unet.rundir}", + f"-eMUNET_NODENAME={self.name}", + "-i", + ] + self.__base_cmd_pty = list(self.__base_cmd) # copy list to pty + self.__base_cmd.append(self.container_id) # end regular list + self.__base_cmd_pty.append("-t") # add pty flags + self.__base_cmd_pty.append(self.container_id) # end pty list + # self.set_pre_cmd(self.__base_cmd, self.__base_cmd_pty) # set both pre_cmd + + self.logger.info("%s: started container", self.name) + + self.pytest_hook_open_shell() + + return self.cmd_p + + async def async_cleanup_cmd(self): + """Run the configured cleanup commands for this node.""" + self.cleanup_called = True + + if "cleanup-cmd" not in self.config: + return + + if not self.cmd_p: + self.logger.warning("async_cleanup_cmd: container no longer running") + return + + return await self._async_cleanup_cmd() + + def cmd_completed(self, future): + try: + log = self.logger.debug if self.deleting else self.logger.warning + n = future.result() + if self.deleting: + log("contianer `cmd:` result: %s", n) + else: + log( + "contianer `cmd:` exited early, " + "try adding `tail -f /dev/null` to `cmd:`, result: %s", + n, + ) + except asyncio.CancelledError as error: + # Should we stop the container if we have one? or since we are canceled + # we know we will be deleting soon? + self.logger.warning( + "node container cmd wait() canceled: %s:%s", future, error + ) + self.cmd_p = None + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + + if contid := self.container_id: + try: + if not self.cleanup_called: + self.logger.debug("calling user cleanup cmd") + await self.async_cleanup_cmd() + except Exception as error: + self.logger.warning( + "Got an error during delete from async_cleanup_cmd: %s", error + ) + + # Clear the container_id field we want to act like a namespace now. + self.container_id = None + + o = "" + e = "" + if self.cmd_p: + self.logger.debug("podman stop on container: %s", contid) + if (rc := self.cmd_p.returncode) is None: + rc, o, e = await self.async_cmd_status_nsonly( + [get_exec_path_host("podman"), "stop", "--time=2", contid] + ) + if rc and rc < 128: + self.logger.warning( + "%s: podman stop on cmd failed: %s", + self, + cmd_error(rc, o, e), + ) + else: + # It's gone + self.cmd_p = None + + # now remove the container + self.logger.debug("podman rm on container: %s", contid) + rc, o, e = await self.async_cmd_status_nsonly( + [get_exec_path_host("podman"), "rm", contid] + ) + if rc: + self.logger.warning( + "%s: podman rm failed: %s", self, cmd_error(rc, o, e) + ) + else: + self.logger.debug( + "podman removed container %s: %s", contid, cmd_error(rc, o, e) + ) + + await super()._async_delete() + + +class L3QemuVM(L3NodeMixin, LinuxNamespace): + """An VM (qemu) based L3 node.""" + + def __init__(self, name, config, **kwargs): + """Create a Container Node.""" + self.cont_exec_paths = {} + self.launch_p = None + self.qemu_config = config["qemu"] + self.extra_mounts = [] + assert self.qemu_config + self.cmdrepl = None + self.conrepl = None + self.is_kvm = False + self.monrepl = None + self.tapfds = {} + self.cpu_thread_map = {} + + self.tapnames = {} + + self.use_ssh = False + self.__base_cmd = [] + self.__base_cmd_pty = [] + + super().__init__(name=name, config=config, pid=False, **kwargs) + + self.sockdir = self.rundir.joinpath("s") + self.cmd_raises(f"mkdir -p {self.sockdir}") + + self.qemu_config = config_subst( + self.qemu_config, + name=self.name, + rundir=os.path.join(self.rundir, self.name), + configdir=self.unet.config_dirname, + ) + self.ssh_keyfile = self.qemu_config.get("sshkey") + + @property + def is_vm(self): + return True + + def __setup_ssh(self): + if not self.ssh_keyfile: + self.logger.warning("%s: No sshkey config", self) + return False + if not self.mgmt_ip and not self.mgmt_ip6: + self.logger.warning("%s: No mgmt IP to ssh to", self) + return False + mgmt_ip = self.mgmt_ip if self.mgmt_ip else self.mgmt_ip6 + + # + # Since we have a keyfile shouldn't need to sudo + # self.user = os.environ.get("SUDO_USER", "") + # if not self.user: + # self.user = getpass.getuser() + # self.__base_cmd = [ + # get_exec_path_host("sudo"), + # "-E", + # f"-u{self.user}", + # get_exec_path_host("ssh"), + # ] + # + port = 22 + self.__base_cmd = [get_exec_path_host("ssh")] + if port != 22: + self.__base_cmd.append(f"-p{port}") + self.__base_cmd.append("-i") + self.__base_cmd.append(self.ssh_keyfile) + self.__base_cmd.append("-q") + self.__base_cmd.append("-oStrictHostKeyChecking=no") + self.__base_cmd.append("-oUserKnownHostsFile=/dev/null") + # Would be nice but has to be accepted by server config so not very useful. + # self.__base_cmd.append("-oSendVar='TEST'") + self.__base_cmd_pty = list(self.__base_cmd) + self.__base_cmd_pty.append("-t") + + user = self.qemu_config.get("sshuser", "root") + self.__base_cmd.append(f"{user}@{mgmt_ip}") + self.__base_cmd.append("--") + self.__base_cmd_pty.append(f"{user}@{mgmt_ip}") + # self.__base_cmd_pty.append("--") + return True + + def _get_cmd_as_list(self, cmd): + """Given a list or string return a list form for execution. + + If cmd is a string then [cmd] is returned, for most other + node types ["bash", "-c", cmd] is returned but in our case + ssh is the shell. + + Args: + cmd: list or string representing the command to execute. + str_shell: if True and `cmd` is a string then run the + command using bash -c + Returns: + list of commands to execute. + """ + if self.use_ssh and self.launch_p: + return [cmd] if isinstance(cmd, str) else cmd + return super()._get_cmd_as_list(cmd) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + if ns_only: + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + if not self.launch_p: + self.logger.debug("%s: Running command in namespace b/c no VM", self) + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + if not self.use_ssh: + self.logger.debug( + "%s: Running command in namespace b/c no SSH configured", self + ) + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + pre_cmd = self.unet._get_pre_cmd(use_str, use_pty, ns_only=True) + + # This is going to run in the process namespaces. + # We really want it to run in the munet namespace which will + # be different unless unshare_inline was used. + # + # XXX grab the env from kwargs and add to podman exec + # env = kwargs.get("env", {}) + if use_pty: + pre_cmd = pre_cmd + self.__base_cmd_pty + else: + pre_cmd = pre_cmd + self.__base_cmd + return shlex.join(pre_cmd) if use_str else pre_cmd + + async def moncmd(self): + """Uses internal REPL to send cmmand to qemu monitor and get reply.""" + + def tmpfs_mount(self, inner): + # eventually would be nice to support live mounting + self.logger.debug("Mounting tmpfs on %s", inner) + self.extra_mounts.append(("", inner, "")) + + # + # bind_mount is actually being used to mount into the namespace + # + # def bind_mount(self, outer, inner): + # # eventually would be nice to support live mounting + # assert not self.container_id + # if self.test_host("-f", outer): + # self.logger.warning("Can't bind mount files with L3QemuVM: %s", outer) + # return + # self.logger.debug("Bind mounting %s on %s", outer, inner) + # if not self.test_host("-e", outer): + # self.cmd_raises(f"mkdir -p {outer}") + # self.extra_mounts.append((outer, inner, "")) + + def mount_volumes(self): + """Mount volumes from the config.""" + args = [] + for m in self.config.get("volumes", []): + if not isinstance(m, str): + continue + s = m.split(":", 1) + if len(s) == 1: + args.append(("", s[0], "")) + else: + spath = s[0] + spath = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), spath + ) + ) + if not self.test_nsonly("-e", spath): + self.cmd_raises_nsonly(f"mkdir -p {spath}") + args.append((spath, s[1], "")) + + for m in self.config.get("mounts", []): + src = m.get("src", m.get("source", "")) + if src: + src = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), src + ) + ) + if not self.test_nsonly("-e", src): + self.cmd_raises_nsonly(f"mkdir -p {src}") + dst = m.get("dst", m.get("destination")) + assert dst, "destination path required for mount" + + margs = [] + for k, v in m.items(): + if k in ["destination", "dst", "source", "src"]: + continue + if k == "type": + assert v in ["bind", "tmpfs"] + continue + if not v: + margs.append(k) + else: + margs.append(f"{k}={v}") + args.append((src, dst, ",".join(margs))) + + if args: + self.extra_mounts += args + + async def run_cmd(self): + """Run the configured commands for this node inside VM.""" + self.logger.debug( + "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir) + ) + + cmd = self.config.get("cmd", "").strip() + if not cmd: + self.logger.debug("%s: no `cmd` to run", self) + return None + + shell_cmd = self.config.get("shell", "/bin/bash") + if not isinstance(shell_cmd, str): + if shell_cmd: + shell_cmd = "/bin/bash" + else: + shell_cmd = "" + + if shell_cmd: + cmd = cmd.rstrip() + cmd = f"#!{shell_cmd}\n" + cmd + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmd += "\n" + + # Write a copy to the rundir + cmdpath = os.path.join(self.rundir, "cmd.shebang") + with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile: + cmdfile.write(cmd) + commander.cmd_raises(f"chmod 755 {cmdpath}") + + # Now write a copy inside the VM + self.conrepl.cmd_status("cat > /tmp/cmd.shebang << EOF\n" + cmd + "\nEOF") + self.conrepl.cmd_status("chmod 755 /tmp/cmd.shebang") + cmds = "/tmp/cmd.shebang" + else: + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmds = cmd + + # class future_proc: + # """Treat awaitable minimally as a proc.""" + # def __init__(self, aw): + # self.aw = aw + # # XXX would be nice to have a real value here + # self.returncode = 0 + # async def wait(self): + # if self.aw: + # return await self.aw + # return None + + class now_proc: + """Treat awaitable minimally as a proc.""" + + def __init__(self, output): + self.output = output + self.returncode = 0 + + async def wait(self): + return self.output + + if self.cmdrepl: + # self.cmd_p = future_proc( + # # We need our own console here b/c this is async and not returning + # # immediately + # # self.cmdrepl.run_command(cmds, timeout=120, async_=True) + # self.cmdrepl.run_command(cmds, timeout=120) + # ) + + # When run_command supports async_ arg we can use the above... + self.cmd_p = now_proc(self.cmdrepl.run_command(cmds, timeout=120)) + + # stdout and err both combined into logfile from the spawned repl + stdout = os.path.join(self.rundir, "_cmdcon-log.txt") + self.pytest_hook_run_cmd(stdout, None) + else: + # If we only have a console we can't run in parallel, so run to completion + self.cmd_p = now_proc(self.conrepl.run_command(cmds, timeout=120)) + + return self.cmd_p + + # InterfaceMixin override + # We need a name unique in the shared namespace. + def get_ns_ifname(self, ifname): + return self.name + ifname + + async def add_host_intf(self, hname, lname, mtu=None): + # L3QemuVM needs it's own add_host_intf for macvtap, We need to create the tap + # in the host then move that interface so that the ifindex/devfile are + # different. + + if hname in self.host_intfs: + return + + self.host_intfs[hname] = lname + index = len(self.host_intfs) + + tapindex = self.unet.tapcount + self.unet.tapcount = self.unet.tapcount + 1 + + tapname = f"tap{tapindex}" + self.tapnames[hname] = tapname + + mac = f"02:bb:bb:bb:{index:02x}:{self.id:02x}" + self.tapmacs[hname] = mac + + self.unet.rootcmd.cmd_raises( + f"ip link add link {hname} name {tapname} type macvtap" + ) + if mtu: + self.unet.rootcmd.cmd_raises(f"ip link set {tapname} mtu {mtu}") + self.unet.rootcmd.cmd_raises(f"ip link set {tapname} address {mac} up") + ifindex = self.unet.rootcmd.cmd_raises( + f"cat /sys/class/net/{tapname}/ifindex" + ).strip() + # self.unet.rootcmd.cmd_raises(f"ip link set {tapname} netns {self.pid}") + + tapfile = f"/dev/tap{ifindex}" + fd = os.open(tapfile, os.O_RDWR) + self.tapfds[hname] = fd + self.logger.info( + "%s: Add host intf: created macvtap interface %s (%s) on %s fd %s", + self, + tapname, + tapfile, + hname, + fd, + ) + + async def rem_host_intf(self, hname): + tapname = self.tapnames[hname] + self.unet.rootcmd.cmd_raises(f"ip link set {tapname} down") + self.unet.rootcmd.cmd_raises(f"ip link delete {tapname} type macvtap") + del self.tapnames[hname] + del self.host_intfs[hname] + + async def create_tap(self, index, ifname, mtu=None, driver="virtio-net-pci"): + # XXX we shouldn't be doign a tap on a bridge with a veth + # we should just be using a tap created earlier which was connected to the + # bridge. Except we need to handle the case of p2p qemu <-> namespace + # + ifname = self.get_ns_ifname(ifname) + brname = f"{self.name}br{index}" + + tapindex = self.unet.tapcount + self.unet.tapcount += 1 + + mac = f"02:aa:aa:aa:{index:02x}:{self.id:02x}" + # nic = "tap,model=virtio-net-pci" + # qemu -net nic,model=virtio,addr=1a:46:0b:ca:bc:7b -net tap,fd=3 3<>/dev/tap11 + self.cmd_raises(f"ip address flush dev {ifname}") + self.cmd_raises(f"ip tuntap add tap{tapindex} mode tap") + self.cmd_raises(f"ip link add name {brname} type bridge") + self.cmd_raises(f"ip link set dev {ifname} master {brname}") + self.cmd_raises(f"ip link set dev tap{tapindex} master {brname}") + if mtu: + self.cmd_raises(f"ip link set dev tap{tapindex} mtu {mtu}") + self.cmd_raises(f"ip link set dev {ifname} mtu {mtu}") + self.cmd_raises(f"ip link set dev tap{tapindex} up") + self.cmd_raises(f"ip link set dev {ifname} up") + self.cmd_raises(f"ip link set dev {brname} up") + dev = f"{driver},netdev=n{index},mac={mac}" + return [ + "-netdev", + f"tap,id=n{index},ifname=tap{tapindex},script=no,downscript=no", + "-device", + dev, + ] + + async def mount_mounts(self): + """Mount any shared directories.""" + self.logger.info("Mounting shared directories") + con = self.conrepl + for i, m in enumerate(self.extra_mounts): + outer, mp, uargs = m + if not outer: + con.cmd_raises(f"mkdir -p {mp}") + margs = f"-o {uargs}" if uargs else "" + con.cmd_raises(f"mount {margs} -t tmpfs tmpfs {mp}") + continue + + uargs = "" if uargs is None else uargs + margs = "trans=virtio" + if uargs: + margs += f",{uargs}" + self.logger.info("Mounting %s on %s with %s", outer, mp, margs) + con.cmd_raises(f"mkdir -p {mp}") + con.cmd_raises(f"mount -t 9p -o {margs} shared{i} {mp}") + + async def renumber_interfaces(self): + """Re-number the interfaces. + + After VM comes up need to renumber the interfaces now on the inside. + """ + self.logger.info("Renumbering interfaces") + con = self.conrepl + con.cmd_raises("sysctl -w net.ipv4.ip_forward=1") + if self.unet.ipv6_enable: + self.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1") + for ifname in sorted(self.intfs): + conn = find_with_kv(self.config.get("connections"), "name", ifname) + to = conn["to"] + switch = self.unet.switches.get(to) + mtu = conn.get("mtu") + if not mtu and switch: + mtu = switch.config.get("mtu") + if mtu: + con.cmd_raises(f"ip link set {ifname} mtu {mtu}") + con.cmd_raises(f"ip link set {ifname} up") + # In case there was some preconfig e.g., cloud-init + con.cmd_raises(f"ip -4 addr flush dev {ifname}") + sw_is_nat = switch and hasattr(switch, "is_nat") and switch.is_nat + if ifaddr := self.get_intf_addr(ifname, ipv6=False): + con.cmd_raises(f"ip addr add {ifaddr} dev {ifname}") + if sw_is_nat: + # In case there was some preconfig e.g., cloud-init + con.cmd_raises("ip route flush exact default") + con.cmd_raises(f"ip route add default via {switch.ip_address}") + if ifaddr := self.get_intf_addr(ifname, ipv6=True): + con.cmd_raises(f"ip -6 addr add {ifaddr} dev {ifname}") + if sw_is_nat: + # In case there was some preconfig e.g., cloud-init + con.cmd_raises("ip -6 route flush exact default") + con.cmd_raises(f"ip -6 route add default via {switch.ip6_address}") + con.cmd_raises("ip link set lo up") + + if self.unet.cfgopt.getoption("--coverage"): + con.cmd_raises("mount -t debugfs none /sys/kernel/debug") + + async def gather_coverage_data(self): + con = self.conrepl + + gcda = "/sys/kernel/debug/gcov" + tmpdir = con.cmd_raises("mktemp -d").strip() + dest = "/gcov-data.tgz" + con.cmd_raises(rf"find {gcda} -type d -exec mkdir -p {tmpdir}/{{}} \;") + con.cmd_raises( + rf"find {gcda} -name '*.gcda' -exec sh -c 'cat < $0 > {tmpdir}/$0' {{}} \;" + ) + con.cmd_raises( + rf"find {gcda} -name '*.gcno' -exec sh -c 'cp -d $0 {tmpdir}/$0' {{}} \;" + ) + con.cmd_raises(rf"tar cf - -C {tmpdir} sys | gzip -c > {dest}") + con.cmd_raises(rf"rm -rf {tmpdir}") + self.logger.info("Saved coverage data in VM at %s", dest) + if self.use_ssh: + ldest = os.path.join(self.rundir, "gcov-data.tgz") + self.cmd_raises(["/bin/cat", dest], stdout=open(ldest, "wb")) + self.logger.info("Saved coverage data on host at %s", ldest) + + async def _opencons( + self, + *cnames, + prompt=None, + is_bourne=True, + user="root", + password="", + expects=None, + sends=None, + timeout=-1, + ): + """Open consoles based on socket file names.""" + timeo = Timeout(timeout) + cons = [] + for cname in cnames: + sockpath = os.path.join(self.sockdir, cname) + connected = False + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + while self.launch_p.returncode is None and not timeo.is_expired(): + try: + sock.connect(sockpath) + connected = True + break + except OSError as error: + if error.errno == errno.ENOENT: + self.logger.debug("waiting for console socket: %s", sockpath) + else: + self.logger.warning( + "can't open console socket: %s", error.strerror + ) + raise + elapsed = int(timeo.elapsed()) + if elapsed <= 3: + await asyncio.sleep(0.25) + else: + self.logger.info( + "%s: launch (qemu) taking more than %ss", self, elapsed + ) + await asyncio.sleep(1) + + if connected: + if prompt is None: + prompt = r"(^|\r\n)[^#\$]*[#\$] " + cons.append( + await self.console( + sock, + prompt=prompt, + is_bourne=is_bourne, + user=user, + password=password, + use_pty=False, + logfile_prefix=cname, + will_echo=True, + expects=expects, + sends=sends, + timeout=timeout, + trace=True, + ) + ) + elif self.launch_p.returncode is not None: + self.logger.warning( + "%s: launch (qemu) exited quickly (%ss) rc: %s", + self, + timeo.elapsed(), + self.launch_p.returncode, + ) + raise Exception("Qemu launch exited early") + elif timeo.is_expired(): + self.logger.critical( + "%s: timeout (%ss) waiting for qemu to start", + self, + timeo.elapsed(), + ) + assert not timeo.is_expired() + + return cons + + async def set_cpu_affinity(self, afflist): + for i, aff in enumerate(afflist): + if not aff: + continue + # affmask = convert_ranges_to_bitmask(aff) + if i not in self.cpu_thread_map: + logging.warning("affinity %s given for missing vcpu %s", aff, i) + continue + logging.info("setting vcpu %s affinity to %s", i, aff) + tid = self.cpu_thread_map[i] + self.cmd_raises_nsonly(f"taskset -cp {aff} {tid}") + + async def launch(self): + """Launch qemu.""" + self.logger.info("%s: Launch Qemu", self) + + qc = self.qemu_config + cc = qc.get("console", {}) + bootd = "d" if "iso" in qc else "c" + # args = [get_exec_path_host("qemu-system-x86_64"), + # "-nodefaults", "-boot", bootd] + args = [get_exec_path_host("qemu-system-x86_64"), "-boot", bootd] + + args += ["-machine", "q35"] + + if qc.get("kvm"): + rc, _, e = await self.async_cmd_status_nsonly("ls -l /dev/kvm") + if rc: + self.logger.warning("Can't enable KVM no /dev/kvm: %s", e) + else: + # [args += ["-enable-kvm", "-cpu", "host"] + # uargs += ["-accel", "kvm", "-cpu", "Icelake-Server-v5"] + args += ["-accel", "kvm", "-cpu", "host"] + + if ncpu := qc.get("ncpu"): + # args += ["-smp", f"sockets={ncpu}"] + args += ["-smp", f"cores={ncpu}"] + # args += ["-smp", f"{ncpu},sockets={ncpu},cores=1,threads=1"] + + args.extend(["-m", str(qc.get("memory", "512M"))]) + + if "bios" in qc: + if qc["bios"] == "open-firmware": + args.extend(["-bios", "/usr/share/qemu/OVMF.fd"]) + else: + args.extend(["-bios", qc["bios"]]) + if "kernel" in qc: + args.extend(["-kernel", qc["kernel"]]) + if "initrd" in qc: + args.extend(["-initrd", qc["initrd"]]) + if "iso" in qc: + args.extend(["-cdrom", qc["iso"]]) + + # we only have append if we have a kernel + if "kernel" in qc: + args.append("-append") + root = qc.get("root", "/dev/ram0") + # Only 1 serial console the other ports (ttyS[123] hvc[01]) should have + # gettys in inittab + append = f"root={root} rw console=ttyS0" + if "cmdline-extra" in qc: + append += f" {qc['cmdline-extra']}" + args.append(append) + + if "extra-args" in qc: + if isinstance(qc["extra-args"], list): + args.extend(qc["extra-args"]) + else: + args.extend(shlex.split(qc["extra-args"])) + + # Walk the list of connections in order so we attach them the same way + pass_fds = [] + nnics = 0 + pciaddr = 3 + for index, conn in enumerate(self.config["connections"]): + devaddr = conn.get("physical", "") + hostintf = conn.get("hostintf", "") + if devaddr: + # if devaddr in self.tapmacs: + # mac = f",mac={self.tapmacs[devaddr]}" + # else: + # mac = "" + args += ["-device", f"vfio-pci,host={devaddr},addr={pciaddr}"] + elif hostintf: + fd = self.tapfds[hostintf] + mac = self.tapmacs[hostintf] + args += [ + "-nic", + f"tap,model=virtio-net-pci,mac={mac},fd={fd},addr={pciaddr}", + ] + pass_fds.append(fd) + nnics += 1 + elif not hostintf: + driver = conn.get("driver", "virtio-net-pci") + mtu = conn.get("mtu") + if not mtu and conn["to"] in self.unet.switches: + mtu = self.unet.switches[conn["to"]].config.get("mtu") + tapargs = await self.create_tap( + index, conn["name"], mtu=mtu, driver=driver + ) + tapargs[-1] += f",addr={pciaddr}" + args += tapargs + nnics += 1 + pciaddr += 1 + if not nnics: + args += ["-nic", "none"] + + dtpl = qc.get("disk-template") + diskpath = disk = qc.get("disk") + if dtpl and not disk: + disk = qc["disk"] = f"{self.name}-{os.path.basename(dtpl)}" + diskpath = os.path.join(self.rundir, disk) + if self.path_exists(diskpath): + logging.debug("Disk '%s' file exists, using.", diskpath) + else: + dtplpath = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), dtpl + ) + ) + logging.info("Create disk '%s' from template '%s'", diskpath, dtplpath) + self.cmd_raises( + f"qemu-img create -f qcow2 -F qcow2 -b {dtplpath} {diskpath}" + ) + + if diskpath: + args.extend( + ["-drive", f"file={diskpath},if=none,id=sata-disk0,format=qcow2"] + ) + args.extend(["-device", "ahci,id=ahci"]) + args.extend(["-device", "ide-hd,bus=ahci.0,drive=sata-disk0"]) + + use_stdio = cc.get("stdio", True) + has_cmd = self.config.get("cmd") + use_cmdcon = has_cmd and use_stdio + + # + # Any extra serial/console ports beyond thw first, require entries in + # inittab to have getty running on them, modify inittab + # + # Use -serial stdio for output only, and as the first serial console + # which kernel uses for printk, as it has serious issues with dropped + # input chars for some reason. + # + # 4 serial ports (max), we'll add extra ports using virtual consoles. + _sd = self.sockdir + if use_stdio: + args += ["-serial", "stdio"] + args += ["-serial", f"unix:{_sd}/_console,server,nowait"] + if use_cmdcon: + args += [ + "-serial", + f"unix:{_sd}/_cmdcon,server,nowait", + ] + args += [ + "-serial", + f"unix:{_sd}/console,server,nowait", + # A 2 virtual consoles - /dev/hvc[01] + # Requires CONFIG_HVC_DRIVER=y CONFIG_VIRTIO_CONSOLE=y + "-device", + "virtio-serial", # serial console bus + "-chardev", + f"socket,path={_sd}/vcon0,server=on,wait=off,id=vcon0", + "-chardev", + f"socket,path={_sd}/vcon1,server=on,wait=off,id=vcon1", + "-device", + "virtconsole,chardev=vcon0", + "-device", + "virtconsole,chardev=vcon1", + # 2 monitors + "-monitor", + f"unix:{_sd}/_monitor,server,nowait", + "-monitor", + f"unix:{_sd}/monitor,server,nowait", + "-gdb", + f"unix:{_sd}/gdbserver,server,nowait", + ] + + for i, m in enumerate(self.extra_mounts): + args += [ + "-virtfs", + f"local,path={m[0]},mount_tag=shared{i},security_model=passthrough", + ] + + args += ["-nographic"] + + # + # Launch Qemu + # + + stdout = open(os.path.join(self.rundir, "qemu.out"), "wb") + stderr = open(os.path.join(self.rundir, "qemu.err"), "wb") + self.launch_p = await self.async_popen( + args, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + pass_fds=pass_fds, + # We don't need this here b/c we are only ever running qemu and that's all + # we need to kill for cleanup + # XXX reconcile this + start_new_session=True, # allows us to signal all children to exit + ) + + self.pytest_hook_run_cmd(stdout, stderr) + + # We've passed these on, so don't need these open here anymore. + for fd in pass_fds: + os.close(fd) + + self.logger.debug("%s: async_popen => %s", self, self.launch_p.pid) + + confiles = ["_console"] + if use_cmdcon: + confiles.append("_cmdcon") + + # + # Connect to the console socket, retrying + # + prompt = cc.get("prompt") + cons = await self._opencons( + *confiles, + prompt=prompt, + is_bourne=not bool(prompt), + user=cc.get("user", "root"), + password=cc.get("password", ""), + expects=cc.get("expects"), + sends=cc.get("sends"), + timeout=int(cc.get("timeout", 60)), + ) + self.conrepl = cons[0] + if use_cmdcon: + self.cmdrepl = cons[1] + self.monrepl = await self.monitor(os.path.join(self.sockdir, "_monitor")) + + # the monitor output has super annoying ANSI escapes in it + + output = self.monrepl.cmd_nostatus("info status") + self.logger.info("VM status: %s", output) + + output = self.monrepl.cmd_nostatus("info kvm") + self.logger.info("KVM status: %s", output) + + # + # Set thread affinity + # + output = self.monrepl.cmd_nostatus("info cpus") + matches = re.findall(r"CPU #(\d+): *thread_id=(\d+)", output) + self.cpu_thread_map = {int(k): int(v) for k, v in matches} + if cpuaff := self.qemu_config.get("cpu-affinity"): + await self.set_cpu_affinity(cpuaff) + + self.is_kvm = "disabled" not in output + + if qc.get("unix-os", True): + await self.renumber_interfaces() + + if self.extra_mounts: + await self.mount_mounts() + + self.use_ssh = bool(self.ssh_keyfile) + if self.use_ssh: + self.use_ssh = self.__setup_ssh() + + self.pytest_hook_open_shell() + + return self.launch_p + + def launch_completed(self, future): + self.logger.debug("%s: launch (qemu) completed called", self) + self.use_ssh = False + try: + n = future.result() + self.logger.debug("%s: node launch (qemu) completed result: %s", self, n) + except asyncio.CancelledError as error: + self.logger.debug( + "%s: node launch (qemu) cmd wait() canceled: %s", future, error + ) + + async def cleanup_qemu(self): + """Launch qemu.""" + if self.launch_p: + await self.async_cleanup_proc(self.launch_p) + + async def async_cleanup_cmd(self): + """Run the configured cleanup commands for this node.""" + self.cleanup_called = True + + if "cleanup-cmd" not in self.config: + return + + if not self.launch_p: + self.logger.warning("async_cleanup_cmd: qemu no longer running") + return + + raise NotImplementedError("Needs to be like run_cmd") + # return await self._async_cleanup_cmd() + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + + # Need to cleanup early b/c it is running on the VM + if self.cmd_p: + await self.async_cleanup_proc(self.cmd_p) + self.cmd_p = None + + try: + # Need to cleanup early b/c it is running on the VM + if not self.cleanup_called: + await self.async_cleanup_cmd() + except Exception as error: + self.logger.warning( + "Got an error during delete from async_cleanup_cmd: %s", error + ) + + try: + if not self.launch_p: + self.logger.warning("async_delete: qemu is not running") + else: + await self.cleanup_qemu() + except Exception as error: + self.logger.warning("%s: failued to cleanup qemu process: %s", self, error) + + await super()._async_delete() + + +class Munet(BaseMunet): + """Munet.""" + + def __init__( + self, + rundir=None, + config=None, + pid=True, + logger=None, + **kwargs, + ): + # logging.warning("Munet") + + if not rundir: + rundir = "/tmp/munet" + + if logger is None: + logger = logging.getLogger("munet.unet") + + super().__init__("munet", pid=pid, rundir=rundir, logger=logger, **kwargs) + + self.built = False + self.tapcount = 0 + + self.cmd_raises(f"mkdir -p {self.rundir} && chmod 755 {self.rundir}") + self.set_ns_cwd(self.rundir) + + if not config: + config = {} + self.config = config + if "config_pathname" in config: + self.config_pathname = os.path.realpath(config["config_pathname"]) + self.config_dirname = os.path.dirname(self.config_pathname) + else: + self.config_pathname = "" + self.config_dirname = "" + + # Done in BaseMunet now + # # We need some way to actually get back to the root namespace + # if not self.isolated: + # self.rootcmd = commander + # else: + # spid = str(pid) + # nsflags = (f"--mount={self.proc_path / spid / 'ns/mnt'}", + # f"--net={self.proc_path / spid / 'ns/net'}", + # f"--uts={self.proc_path / spid / 'ns/uts'}", + # f"--ipc={self.proc_path / spid / 'ns/ipc'}", + # f"--cgroup={self.proc_path / spid / 'ns/cgroup'}", + # f"--pid={self.proc_path / spid / 'ns/net'}", + # self.rootcmd = SharedNamespace("host", pid=1, nsflags=nsflags) + + # Save the namespace pid + with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f: + f.write(f"{self.pid}\n") + + with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f: + f.write(f'{" ".join([str(x) for x in self.pids])}\n') + + hosts_file = os.path.join(self.rundir, "hosts.txt") + with open(hosts_file, "w", encoding="ascii") as hf: + hf.write( + f"""127.0.0.1\tlocalhost {self.name} +::1\tip6-localhost ip6-loopback +fe00::0\tip6-localnet +ff00::0\tip6-mcastprefix +ff02::1\tip6-allnodes +ff02::2\tip6-allrouters +""" + ) + self.bind_mount(hosts_file, "/etc/hosts") + + # Common CLI commands for any topology + cdict = { + "commands": [ + { + "name": "pcap", + "format": "pcap NETWORK", + "help": ( + "capture packets from NETWORK into file capture-NETWORK.pcap" + " the command is run within a new window which also shows" + " packet summaries. NETWORK can also be an interface specified" + " as HOST:INTF. To capture inside the host namespace." + ), + "exec": "tshark -s 9200 -i {0} -P -w capture-{0}.pcap", + "top-level": True, + "new-window": {"background": True}, + }, + { + "name": "nsterm", + "format": "nsterm HOST [HOST ...]", + "help": ( + "open terminal[s] in the namespace only" + " (outside containers or VM), * for all" + ), + "exec": "bash", + "new-window": {"ns_only": True}, + }, + { + "name": "term", + "format": "term HOST [HOST ...]", + "help": "open terminal[s] (TMUX or XTerm) on HOST[S], * for all", + "exec": "bash", + "new-window": True, + }, + { + "name": "xterm", + "format": "xterm HOST [HOST ...]", + "help": "open XTerm[s] on HOST[S], * for all", + "exec": "bash", + "new-window": { + "forcex": True, + }, + }, + { + "name": "sh", + "format": "[HOST ...] sh <SHELL-COMMAND>", + "help": "execute <SHELL-COMMAND> on hosts", + "exec": "{}", + }, + { + "name": "shi", + "format": "[HOST ...] shi <INTERACTIVE-COMMAND>", + "help": "execute <INTERACTIVE-COMMAND> on HOST[s]", + "exec": "{}", + "interactive": True, + }, + { + "name": "stdout", + "exec": ( + "[ -e %RUNDIR%/qemu.out ] && tail -F %RUNDIR%/qemu.out " + "|| tail -F %RUNDIR%/cmd.out" + ), + "format": "stdout HOST [HOST ...]", + "help": "tail -f on the stdout of the qemu/cmd for this node", + "new-window": True, + }, + { + "name": "stderr", + "exec": ( + "[ -e %RUNDIR%/qemu.err ] && tail -F %RUNDIR%/qemu.err " + "|| tail -F %RUNDIR%/cmd.err" + ), + "format": "stderr HOST [HOST ...]", + "help": "tail -f on the stdout of the qemu/cmd for this node", + "new-window": True, + }, + ] + } + + cli.add_cli_config(self, cdict) + + if "cli" in config: + cli.add_cli_config(self, config["cli"]) + + if "topology" not in self.config: + self.config["topology"] = {} + + self.topoconf = self.config["topology"] + self.ipv6_enable = self.topoconf.get("ipv6-enable", False) + + if self.isolated: + if not self.ipv6_enable: + # Disable IPv6 + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=0") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=1") + else: + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=1") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=0") + + # we really need overlay, but overlay-layers (used by overlay-images) + # counts on things being present in overlay so this temp stuff doesn't work. + # if self.isolated: + # # Let's hide podman details + # self.tmpfs_mount("/var/lib/containers/storage/overlay-containers") + + shellopt = self.cfgopt.getoption("--shell") + shellopt = shellopt if shellopt else "" + if shellopt == "all" or "." in shellopt.split(","): + self.run_in_window("bash") + + def __del__(self): + """Catch case of build object but not async_deleted.""" + if hasattr(self, "built"): + if not self.deleting: + logging.critical( + "Munet object deleted without calling `async_delete` for cleanup." + ) + s = super() + if hasattr(s, "__del__"): + s.__del__(self) + + async def _async_build(self, logger=None): + """Build the topology based on config.""" + if self.built: + self.logger.warning("%s: is already built", self) + return + + self.built = True + + # Allow for all networks to be auto-numbered + topoconf = self.topoconf + autonumber = self.autonumber + ipv6_enable = self.ipv6_enable + + # --------------------------------------------- + # Merge Kinds and perform variable substitution + # --------------------------------------------- + + kinds = self.config.get("kinds", {}) + + for name, conf in config_to_dict_with_key(topoconf, "networks", "name").items(): + if kind := conf.get("kind"): + if kconf := kinds[kind]: + conf = merge_kind_config(kconf, conf) + conf = config_subst( + conf, name=name, rundir=self.rundir, configdir=self.config_dirname + ) + if "ip" not in conf and autonumber: + conf["ip"] = "auto" + if "ipv6" not in conf and autonumber and ipv6_enable: + conf["ipv6"] = "auto" + topoconf["networks"][name] = conf + self.add_network(name, conf, logger=logger) + + for name, conf in config_to_dict_with_key(topoconf, "nodes", "name").items(): + if kind := conf.get("kind"): + if kconf := kinds[kind]: + conf = merge_kind_config(kconf, conf) + + config_to_dict_with_key( + conf, "env", "name" + ) # convert list of env objects to dict + + conf = config_subst( + conf, + name=name, + rundir=os.path.join(self.rundir, name), + configdir=self.config_dirname, + ) + topoconf["nodes"][name] = conf + self.add_l3_node(name, conf, logger=logger) + + # ------------------ + # Create connections + # ------------------ + + # Go through all connections and name them so they are sane to the user + # otherwise when we do p2p links the names/ords skip around based oddly + for name, node in self.hosts.items(): + nconf = node.config + if "connections" not in nconf: + continue + nconns = [] + for cconf in nconf["connections"]: + # Replace string only with a dictionary + if isinstance(cconf, str): + splitconf = cconf.split(":", 1) + cconf = {"to": splitconf[0]} + if len(splitconf) == 2: + cconf["name"] = splitconf[1] + # Allocate a name if not already assigned + if "name" not in cconf: + cconf["name"] = node.get_next_intf_name() + nconns.append(cconf) + nconf["connections"] = nconns + + for name, node in self.hosts.items(): + nconf = node.config + if "connections" not in nconf: + continue + for cconf in nconf["connections"]: + # Eventually can add support for unconnected intf here. + if "to" not in cconf: + continue + to = cconf["to"] + if to in self.switches: + switch = self.switches[to] + swconf = find_matching_net_config(name, cconf, switch.config) + await self.add_native_link(switch, node, swconf, cconf) + elif cconf["name"] not in node.intfs: + # Only add the p2p interface if not already there. + other = self.hosts[to] + oconf = find_matching_net_config(name, cconf, other.config) + await self.add_native_link(node, other, cconf, oconf) + + @property + def autonumber(self): + return self.topoconf.get("networks-autonumber", False) + + @autonumber.setter + def autonumber(self, value): + self.topoconf["networks-autonumber"] = bool(value) + + async def add_native_link(self, node1, node2, c1=None, c2=None): + """Add a link between switch and node or 2 nodes.""" + isp2p = False + + c1 = {} if c1 is None else c1 + c2 = {} if c2 is None else c2 + + if node1.name in self.switches: + assert node2.name in self.hosts + elif node2.name in self.switches: + assert node1.name in self.hosts + node1, node2 = node2, node1 + c1, c2 = c2, c1 + else: + # p2p link + assert node1.name in self.hosts + assert node1.name in self.hosts + isp2p = True + + if "name" not in c1: + c1["name"] = node1.get_next_intf_name() + if1 = c1["name"] + + if "name" not in c2: + c2["name"] = node2.get_next_intf_name() + if2 = c2["name"] + + do_add_link = True + for n, c in ((node1, c1), (node2, c2)): + if "hostintf" in c: + await n.add_host_intf(c["hostintf"], c["name"], mtu=c.get("mtu")) + do_add_link = False + elif "physical" in c: + await n.add_phy_intf(c["physical"], c["name"]) + do_add_link = False + if do_add_link: + assert "hostintf" not in c1 + assert "hostintf" not in c2 + assert "physical" not in c1 + assert "physical" not in c2 + + if isp2p: + mtu1 = c1.get("mtu") + mtu2 = c2.get("mtu") + mtu = mtu1 if mtu1 else mtu2 + if mtu1 and mtu2 and mtu1 != mtu2: + self.logger.error("mtus differ for add_link %s != %s", mtu1, mtu2) + else: + mtu = c2.get("mtu") + + super().add_link(node1, node2, if1, if2, mtu=mtu) + + if isp2p: + node1.set_p2p_addr(node2, c1, c2) + else: + node2.set_lan_addr(node1, c2) + + if "physical" not in c1 and not node1.is_vm: + node1.set_intf_constraints(if1, **c1) + if "physical" not in c2 and not node2.is_vm: + node2.set_intf_constraints(if2, **c2) + + def add_l3_node(self, name, config=None, **kwargs): + """Add a node to munet.""" + if config and config.get("image"): + cls = L3ContainerNode + elif config and config.get("qemu"): + cls = L3QemuVM + elif config and config.get("server"): + cls = SSHRemote + kwargs["server"] = config["server"] + kwargs["port"] = int(config.get("server-port", 22)) + if "ssh-identity-file" in config: + kwargs["idfile"] = config.get("ssh-identity-file") + if "ssh-user" in config: + kwargs["user"] = config.get("ssh-user") + if "ssh-password" in config: + kwargs["password"] = config.get("ssh-password") + else: + cls = L3NamespaceNode + return super().add_host(name, cls=cls, config=config, **kwargs) + + def add_network(self, name, config=None, **kwargs): + """Add a l2 or l3 switch to munet.""" + if config is None: + config = {} + + cls = L3Bridge if config.get("ip") else L2Bridge + mtu = kwargs.get("mtu", config.get("mtu")) + return super().add_switch(name, cls=cls, config=config, mtu=mtu, **kwargs) + + async def run(self): + tasks = [] + + hosts = self.hosts.values() + launch_nodes = [x for x in hosts if hasattr(x, "launch")] + launch_nodes = [x for x in launch_nodes if x.config.get("qemu")] + run_nodes = [x for x in hosts if hasattr(x, "has_run_cmd") and x.has_run_cmd()] + ready_nodes = [ + x for x in hosts if hasattr(x, "has_ready_cmd") and x.has_ready_cmd() + ] + + pcapopt = self.cfgopt.getoption("--pcap") + pcapopt = pcapopt if pcapopt else "" + if pcapopt == "all": + pcapopt = self.switches.keys() + if pcapopt: + for pcap in pcapopt.split(","): + if ":" in pcap: + host, intf = pcap.split(":") + pcap = f"{host}-{intf}" + host = self.hosts[host] + else: + host = self + intf = pcap + host.run_in_window( + f"tshark -s 9200 -i {intf} -P -w capture-{pcap}.pcap", + background=True, + title=f"cap:{pcap}", + ) + + if launch_nodes: + # would like a info when verbose here. + logging.debug("Launching nodes") + await asyncio.gather(*[x.launch() for x in launch_nodes]) + + # Watch for launched processes to exit + for node in launch_nodes: + task = asyncio.create_task( + node.launch_p.wait(), name=f"Node-{node.name}-launch" + ) + task.add_done_callback(node.launch_completed) + tasks.append(task) + + if run_nodes: + # would like a info when verbose here. + logging.debug("Running `cmd` on nodes") + await asyncio.gather(*[x.run_cmd() for x in run_nodes]) + + # Watch for run_cmd processes to exit + for node in run_nodes: + task = asyncio.create_task(node.cmd_p.wait(), name=f"Node-{node.name}-cmd") + task.add_done_callback(node.cmd_completed) + tasks.append(task) + + # Wait for nodes to be ready + if ready_nodes: + + async def wait_until_ready(x): + while not await x.async_ready_cmd(): + logging.debug("Waiting for ready on: %s", x) + await asyncio.sleep(0.25) + logging.debug("%s is ready!", x) + + logging.debug("Waiting for ready on nodes: %s", ready_nodes) + _, pending = await asyncio.wait( + [wait_until_ready(x) for x in ready_nodes], timeout=30 + ) + if pending: + logging.warning("Timeout waiting for ready: %s", pending) + for nr in pending: + nr.cancel() + raise asyncio.TimeoutError() + logging.debug("All nodes ready") + + return tasks + + async def _async_delete(self): + from .testing.util import async_pause_test # pylint: disable=C0415 + + self.logger.debug("%s: deleting.", self) + + if self.cfgopt.getoption("--coverage"): + nodes = ( + x for x in self.hosts.values() if hasattr(x, "gather_coverage_data") + ) + try: + await asyncio.gather(*(x.gather_coverage_data() for x in nodes)) + except Exception as error: + logging.warning("Error gathering coverage data: %s", error) + + pause = bool(self.cfgopt.getoption("--pause-at-end")) + pause = pause or bool(self.cfgopt.getoption("--pause")) + if pause: + try: + await async_pause_test("Before MUNET delete") + except KeyboardInterrupt: + print("^C...continuing") + except Exception as error: + self.logger.error("\n...continuing after error: %s", error) + + # XXX should we cancel launch and run tasks? + + try: + await super()._async_delete() + except Exception as error: + self.logger.error("Error cleaning up: %s", error, exc_info=True) + raise + + +async def run_cmd_update_ceos(node, shell_cmd, cmds, cmd): + cmd = cmd.strip() + if shell_cmd or cmd != "/sbin/init": + return cmds, cmd + + # + # Add flash dir and mount it + # + flashdir = os.path.join(node.rundir, "flash") + node.cmd_raises_nsonly(f"mkdir -p {flashdir} && chmod 775 {flashdir}") + cmds += [f"--volume={flashdir}:/mnt/flash"] + + # + # Startup config (if not present already) + # + if startup_config := node.config.get("startup-config", None): + dest = os.path.join(flashdir, "startup-config") + if os.path.exists(dest): + node.logger.info("Skipping copy of startup-config, already present") + else: + source = os.path.join(node.unet.config_dirname, startup_config) + node.cmd_raises_nsonly(f"cp {source} {dest} && chmod 664 {dest}") + + # + # system mac address (if not present already + # + dest = os.path.join(flashdir, "system_mac_address") + if os.path.exists(dest): + node.logger.info("Skipping system-mac generation, already present") + else: + random_arista_mac = "00:1c:73:%02x:%02x:%02x" % ( + random.randint(0, 255), + random.randint(0, 255), + random.randint(0, 255), + ) + system_mac = node.config.get("system-mac", random_arista_mac) + with open(dest, "w", encoding="ascii") as f: + f.write(system_mac + "\n") + node.cmd_raises_nsonly(f"chmod 664 {dest}") + + args = [] + + # Pass special args for the environment variables + if "env" in node.config: + args += [f"systemd.setenv={k}={v}" for k, v in node.config["env"].items()] + + return cmds, [cmd] + args + + +# XXX this is only used by the container code +kind_run_cmd_update = {"ceos": run_cmd_update_ceos} diff --git a/tests/topotests/munet/parser.py b/tests/topotests/munet/parser.py new file mode 100644 index 0000000000..4fc0c75a60 --- /dev/null +++ b/tests/topotests/munet/parser.py @@ -0,0 +1,374 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 30 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module that implements the standalone parser.""" +import asyncio +import importlib.resources +import json +import logging +import logging.config +import os +import subprocess +import sys +import tempfile + +from pathlib import Path + + +try: + import jsonschema # pylint: disable=C0415 + import jsonschema.validators # pylint: disable=C0415 + + from jsonschema.exceptions import ValidationError # pylint: disable=C0415 +except ImportError: + jsonschema = None + +from .config import list_to_dict_with_key +from .native import Munet + + +def get_schema(): + if get_schema.schema is None: + with importlib.resources.path("munet", "munet-schema.json") as datapath: + search = [str(datapath.parent)] + get_schema.schema = get_config(basename="munet-schema", search=search) + return get_schema.schema + + +get_schema.schema = None + +project_root_contains = [ + ".git", + "pyproject.toml", + "tox.ini", + "setup.cfg", + "setup.py", + "pytest.ini", + ".projectile", +] + + +def is_project_root(path: Path) -> bool: + + for contains in project_root_contains: + if path.joinpath(contains).exists(): + return True + return False + + +def find_project_root(config_path: Path, project_root=None): + if project_root is not None: + project_root = Path(project_root) + if project_root in config_path.parents: + return project_root + logging.warning( + "project_root %s is not a common ancestor of config file %s", + project_root, + config_path, + ) + return config_path.parent + for ppath in config_path.parents: + if is_project_root(ppath): + return ppath + return config_path.parent + + +def get_config(pathname=None, basename="munet", search=None, logf=logging.debug): + + cwd = os.getcwd() + + if not search: + search = [cwd] + elif isinstance(search, (str, Path)): + search = [search] + + if pathname: + pathname = os.path.join(cwd, pathname) + if not os.path.exists(pathname): + raise FileNotFoundError(pathname) + else: + for d in search: + logf("%s", f'searching in "{d}" for "{basename}".{{yaml, toml, json}}') + for ext in ("yaml", "toml", "json"): + pathname = os.path.join(d, basename + "." + ext) + if os.path.exists(pathname): + logf("%s", f'Found "{pathname}"') + break + else: + continue + break + else: + raise FileNotFoundError(basename + ".{json,toml,yaml} in " + f"{search}") + + _, ext = pathname.rsplit(".", 1) + + if ext == "json": + config = json.load(open(pathname, encoding="utf-8")) + elif ext == "toml": + import toml # pylint: disable=C0415 + + config = toml.load(pathname) + elif ext == "yaml": + import yaml # pylint: disable=C0415 + + config = yaml.safe_load(open(pathname, encoding="utf-8")) + else: + raise ValueError("Filename does not end with (.json|.toml|.yaml)") + + config["config_pathname"] = os.path.realpath(pathname) + return config + + +def setup_logging(args, config_base="logconf"): + # Create rundir and arrange for future commands to run in it. + + # Change CWD to the rundir prior to parsing config + old = os.getcwd() + os.chdir(args.rundir) + try: + search = [old] + with importlib.resources.path("munet", config_base + ".yaml") as datapath: + search.append(str(datapath.parent)) + + def logf(msg, *p, **k): + if args.verbose: + print("PRELOG: " + msg % p, **k, file=sys.stderr) + + config = get_config(args.log_config, config_base, search, logf=logf) + pathname = config["config_pathname"] + del config["config_pathname"] + + if "info_console" in config["handlers"]: + # mutest case + if args.verbose > 1: + config["handlers"]["console"]["level"] = "DEBUG" + config["handlers"]["info_console"]["level"] = "DEBUG" + elif args.verbose: + config["handlers"]["console"]["level"] = "INFO" + config["handlers"]["info_console"]["level"] = "DEBUG" + elif args.verbose: + # munet case + config["handlers"]["console"]["level"] = "DEBUG" + + # add the rundir path to the filenames + for v in config["handlers"].values(): + filename = v.get("filename") + if not filename: + continue + v["filename"] = os.path.join(args.rundir, filename) + + logging.config.dictConfig(dict(config)) + logging.info("Loaded logging config %s", pathname) + + return config + finally: + os.chdir(old) + + +def append_hosts_files(unet, netname): + if not netname: + return + + entries = [] + for name in ("munet", *list(unet.hosts)): + if name == "munet": + node = unet.switches[netname] + ifname = None + else: + node = unet.hosts[name] + if not hasattr(node, "_intf_addrs"): + continue + ifname = node.get_ifname(netname) + + for b in (False, True): + ifaddr = node.get_intf_addr(ifname, ipv6=b) + if ifaddr and hasattr(ifaddr, "ip"): + entries.append((name, ifaddr.ip)) + + for name in ("munet", *list(unet.hosts)): + node = unet if name == "munet" else unet.hosts[name] + if not hasattr(node, "rundir"): + continue + with open(os.path.join(node.rundir, "hosts.txt"), "a+", encoding="ascii") as hf: + hf.write("\n") + for e in entries: + hf.write(f"{e[1]}\t{e[0]}\n") + + +def validate_config(config, logger, args): + if jsonschema is None: + logger.debug("No validation w/o jsonschema module") + return True + + old = os.getcwd() + if args: + os.chdir(args.rundir) + + try: + validator = jsonschema.validators.Draft202012Validator(get_schema()) + validator.validate(instance=config) + logger.debug("Validated %s", config["config_pathname"]) + return True + except FileNotFoundError as error: + logger.info("No schema found: %s", error) + return False + except ValidationError as error: + logger.info("Validation failed: %s", error) + return False + finally: + if args: + os.chdir(old) + + +def load_kinds(args, search=None): + # Change CWD to the rundir prior to parsing config + cwd = os.getcwd() + if args: + os.chdir(args.rundir) + + args_config = args.kinds_config if args else None + try: + if search is None: + search = [cwd] + with importlib.resources.path("munet", "kinds.yaml") as datapath: + search.insert(0, str(datapath.parent)) + + configs = [] + if args_config: + configs.append(get_config(args_config, "kinds", search=[])) + else: + # prefer directories at the front of the list + for kdir in search: + try: + configs.append(get_config(basename="kinds", search=[kdir])) + except FileNotFoundError: + continue + + kinds = {} + for config in configs: + # XXX need to fix the issue with `connections: ["net0"]` not validating + # if jsonschema is not None: + # validator = jsonschema.validators.Draft202012Validator(get_schema()) + # validator.validate(instance=config) + + kinds_list = config.get("kinds", []) + kinds_dict = list_to_dict_with_key(kinds_list, "name") + if kinds_dict: + logging.info("Loading kinds config from %s", config["config_pathname"]) + if "kinds" in kinds: + kinds["kinds"].update(**kinds_dict) + else: + kinds["kinds"] = kinds_dict + + cli_list = config.get("cli", {}).get("commands", []) + if cli_list: + logging.info("Loading cli comands from %s", config["config_pathname"]) + if "cli" not in kinds: + kinds["cli"] = {} + if "commands" not in kinds["cli"]: + kinds["cli"]["commands"] = [] + kinds["cli"]["commands"].extend(cli_list) + + return kinds + except FileNotFoundError as error: + # if we have kinds in args but the file doesn't exist, raise the error + if args_config is not None: + raise error + return {} + finally: + if args: + os.chdir(cwd) + + +async def async_build_topology( + config=None, + logger=None, + rundir=None, + args=None, + unshare_inline=False, + pytestconfig=None, + search_root=None, + top_level_pidns=True, +): + + if not rundir: + rundir = tempfile.mkdtemp(prefix="unet") + subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True) + + isolated = not args.host if args else True + if not config: + config = get_config(basename="munet") + + # create search directories from common root if given + cpath = Path(config["config_pathname"]).absolute() + project_root = args.project_root if args else None + if not search_root: + search_root = find_project_root(cpath, project_root) + if not search_root: + search = [cpath.parent] + else: + search_root = Path(search_root).absolute() + if search_root in cpath.parents: + search = list(cpath.parents) + if remcount := len(search_root.parents): + search = search[0:-remcount] + + # load kinds along search path and merge into config + kinds = load_kinds(args, search=search) + config_kinds_dict = list_to_dict_with_key(config.get("kinds", []), "name") + config["kinds"] = {**kinds.get("kinds", {}), **config_kinds_dict} + + # mere CLI command from kinds into config as well. + kinds_cli_list = kinds.get("cli", {}).get("commands", []) + config_cli_list = config.get("cli", {}).get("commands", []) + if config_cli_list: + if kinds_cli_list: + config_cli_list.extend(list(kinds_cli_list)) + elif kinds_cli_list: + if "cli" not in config: + config["cli"] = {} + if "commands" not in config["cli"]: + config["cli"]["commands"] = [] + config["cli"]["commands"].extend(list(kinds_cli_list)) + + unet = Munet( + rundir=rundir, + config=config, + pytestconfig=pytestconfig, + isolated=isolated, + pid=top_level_pidns, + unshare_inline=args.unshare_inline if args else unshare_inline, + logger=logger, + ) + + try: + await unet._async_build(logger) # pylint: disable=W0212 + except Exception as error: + logging.critical("Failure building munet topology: %s", error, exc_info=True) + await unet.async_delete() + raise + except KeyboardInterrupt: + await unet.async_delete() + raise + + topoconf = config.get("topology") + if not topoconf: + return unet + + dns_network = topoconf.get("dns-network") + if dns_network: + append_hosts_files(unet, dns_network) + + # Write our current config to the run directory + with open(f"{unet.rundir}/config.json", "w", encoding="utf-8") as f: + json.dump(unet.config, f, indent=2) + + return unet + + +def build_topology(config=None, logger=None, rundir=None, args=None, pytestconfig=None): + return asyncio.run(async_build_topology(config, logger, rundir, args, pytestconfig)) diff --git a/tests/topotests/munet/testing/__init__.py b/tests/topotests/munet/testing/__init__.py new file mode 100644 index 0000000000..63cbfabda9 --- /dev/null +++ b/tests/topotests/munet/testing/__init__.py @@ -0,0 +1 @@ +"""Sub-package supporting munet use in pytest.""" diff --git a/tests/topotests/munet/testing/fixtures.py b/tests/topotests/munet/testing/fixtures.py new file mode 100644 index 0000000000..25039df541 --- /dev/null +++ b/tests/topotests/munet/testing/fixtures.py @@ -0,0 +1,447 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 22 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2022, LabN Consulting, L.L.C +# +"""A module that implements pytest fixtures. + +To use in your project, in your conftest.py add: + + from munet.testing.fixtures import * +""" +import contextlib +import logging +import os + +from pathlib import Path +from typing import Union + +import pytest +import pytest_asyncio + +from ..base import BaseMunet +from ..base import Bridge +from ..base import get_event_loop +from ..cleanup import cleanup_current +from ..cleanup import cleanup_previous +from ..native import L3NodeMixin +from ..parser import async_build_topology +from ..parser import get_config +from .util import async_pause_test +from .util import pause_test + + +@contextlib.asynccontextmanager +async def achdir(ndir: Union[str, Path], desc=""): + odir = os.getcwd() + os.chdir(ndir) + if desc: + logging.debug("%s: chdir from %s to %s", desc, odir, ndir) + try: + yield + finally: + if desc: + logging.debug("%s: chdir back from %s to %s", desc, ndir, odir) + os.chdir(odir) + + +@contextlib.contextmanager +def chdir(ndir: Union[str, Path], desc=""): + odir = os.getcwd() + os.chdir(ndir) + if desc: + logging.debug("%s: chdir from %s to %s", desc, odir, ndir) + try: + yield + finally: + if desc: + logging.debug("%s: chdir back from %s to %s", desc, ndir, odir) + os.chdir(odir) + + +def get_test_logdir(nodeid=None, module=False): + """Get log directory relative pathname.""" + xdist_worker = os.getenv("PYTEST_XDIST_WORKER", "") + mode = os.getenv("PYTEST_XDIST_MODE", "no") + + # nodeid: all_protocol_startup/test_all_protocol_startup.py::test_router_running + # may be missing "::testname" if module is True + if not nodeid: + nodeid = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0] + + cur_test = nodeid.replace("[", "_").replace("]", "_") + if module: + idx = cur_test.rfind("::") + path = cur_test if idx == -1 else cur_test[:idx] + testname = "" + else: + path, testname = cur_test.split("::") + testname = testname.replace("/", ".") + path = path[:-3].replace("/", ".") + + # We use different logdir paths based on how xdist is running. + if mode == "each": + if module: + return os.path.join(path, "worker-logs", xdist_worker) + return os.path.join(path, testname, xdist_worker) + assert mode in ("no", "load", "loadfile", "loadscope"), f"Unknown dist mode {mode}" + return path if module else os.path.join(path, testname) + + +def _push_log_handler(desc, logpath): + logpath = os.path.abspath(logpath) + logging.debug("conftest: adding %s logging at %s", desc, logpath) + root_logger = logging.getLogger() + handler = logging.FileHandler(logpath, mode="w") + fmt = logging.Formatter("%(asctime)s %(levelname)5s: %(message)s") + handler.setFormatter(fmt) + root_logger.addHandler(handler) + return handler + + +def _pop_log_handler(handler): + root_logger = logging.getLogger() + logging.debug("conftest: removing logging handler %s", handler) + root_logger.removeHandler(handler) + + +@contextlib.contextmanager +def log_handler(desc, logpath): + handler = _push_log_handler(desc, logpath) + try: + yield + finally: + _pop_log_handler(handler) + + +# ================= +# Sessions Fixtures +# ================= + + +@pytest.fixture(autouse=True, scope="session") +def session_autouse(): + if "PYTEST_TOPOTEST_WORKER" not in os.environ: + is_worker = False + elif not os.environ["PYTEST_TOPOTEST_WORKER"]: + is_worker = False + else: + is_worker = True + + if not is_worker: + # This is unfriendly to multi-instance + cleanup_previous() + + # We never pop as we want to keep logging + _push_log_handler("session", "/tmp/unet-test/pytest-session.log") + + yield + + if not is_worker: + cleanup_current() + + +# =============== +# Module Fixtures +# =============== + + +@pytest.fixture(autouse=True, scope="module") +def module_autouse(request): + logpath = get_test_logdir(request.node.name, True) + logpath = os.path.join("/tmp/unet-test", logpath, "pytest-exec.log") + with log_handler("module", logpath): + sdir = os.path.dirname(os.path.realpath(request.fspath)) + with chdir(sdir, "module autouse fixture"): + yield + + if BaseMunet.g_unet: + raise Exception("Base Munet was not cleaned up/deleted") + + +@pytest.fixture(scope="module") +def event_loop(): + """Create an instance of the default event loop for the session.""" + loop = get_event_loop() + try: + logging.info("event_loop_fixture: yielding with new event loop watcher") + yield loop + finally: + loop.close() + + +@pytest.fixture(scope="module") +def rundir_module(): + d = os.path.join("/tmp/unet-test", get_test_logdir(module=True)) + logging.debug("conftest: test module rundir %s", d) + return d + + +async def _unet_impl( + _rundir, _pytestconfig, unshare=None, top_level_pidns=None, param=None +): + try: + # Default is not to unshare inline if not specified otherwise + unshare_default = False + pidns_default = True + if isinstance(param, (tuple, list)): + pidns_default = bool(param[2]) if len(param) > 2 else True + unshare_default = bool(param[1]) if len(param) > 1 else False + param = str(param[0]) + elif isinstance(param, bool): + unshare_default = param + param = None + if unshare is None: + unshare = unshare_default + if top_level_pidns is None: + top_level_pidns = pidns_default + + logging.info("unet fixture: basename=%s unshare_inline=%s", param, unshare) + _unet = await async_build_topology( + config=get_config(basename=param) if param else None, + rundir=_rundir, + unshare_inline=unshare, + top_level_pidns=top_level_pidns, + pytestconfig=_pytestconfig, + ) + except Exception as error: + logging.debug( + "unet fixture: unet build failed: %s\nparam: %s", + error, + param, + exc_info=True, + ) + pytest.skip( + f"unet fixture: unet build failed: {error}", allow_module_level=True + ) + raise + + try: + tasks = await _unet.run() + except Exception as error: + logging.debug("unet fixture: unet run failed: %s", error, exc_info=True) + await _unet.async_delete() + pytest.skip(f"unet fixture: unet run failed: {error}", allow_module_level=True) + raise + + logging.debug("unet fixture: containers running") + + # Pytest is supposed to always return even if exceptions + try: + yield _unet + except Exception as error: + logging.error("unet fixture: yield unet unexpected exception: %s", error) + + logging.debug("unet fixture: module done, deleting unet") + await _unet.async_delete() + + # No one ever awaits these so cancel them + logging.debug("unet fixture: cleanup") + for task in tasks: + task.cancel() + + # Reset the class variables so auto number is predictable + logging.debug("unet fixture: resetting ords to 1") + L3NodeMixin.next_ord = 1 + Bridge.next_ord = 1 + + +@pytest.fixture(scope="module") +async def unet(request, rundir_module, pytestconfig): # pylint: disable=W0621 + """A unet creating fixutre. + + The request param is either the basename of the config file or a tuple of the form: + (basename, unshare, top_level_pidns), with the second and third elements boolean and + optional, defaulting to False, True. + """ + param = request.param if hasattr(request, "param") else None + sdir = os.path.dirname(os.path.realpath(request.fspath)) + async with achdir(sdir, "unet fixture"): + async for x in _unet_impl(rundir_module, pytestconfig, param=param): + yield x + + +@pytest.fixture(scope="module") +async def unet_share(request, rundir_module, pytestconfig): # pylint: disable=W0621 + """A unet creating fixutre. + + This share variant keeps munet from unsharing the process to a new namespace so that + root level commands and actions are execute on the host, normally they are executed + in the munet namespace which allowing things like scapy inline in tests to work. + + The request param is either the basename of the config file or a tuple of the form: + (basename, top_level_pidns), the second value is a boolean. + """ + param = request.param if hasattr(request, "param") else None + if isinstance(param, (tuple, list)): + param = (param[0], False, param[1]) + sdir = os.path.dirname(os.path.realpath(request.fspath)) + async with achdir(sdir, "unet_share fixture"): + async for x in _unet_impl( + rundir_module, pytestconfig, unshare=False, param=param + ): + yield x + + +@pytest.fixture(scope="module") +async def unet_unshare(request, rundir_module, pytestconfig): # pylint: disable=W0621 + """A unet creating fixutre. + + This unshare variant has the top level munet unshare the process inline so that + root level commands and actions are execute in a new namespace. This allows things + like scapy inline in tests to work. + + The request param is either the basename of the config file or a tuple of the form: + (basename, top_level_pidns), the second value is a boolean. + """ + param = request.param if hasattr(request, "param") else None + if isinstance(param, (tuple, list)): + param = (param[0], True, param[1]) + sdir = os.path.dirname(os.path.realpath(request.fspath)) + async with achdir(sdir, "unet_unshare fixture"): + async for x in _unet_impl( + rundir_module, pytestconfig, unshare=True, param=param + ): + yield x + + +# ================= +# Function Fixtures +# ================= + + +@pytest.fixture(autouse=True, scope="function") +async def function_autouse(request): + async with achdir( + os.path.dirname(os.path.realpath(request.fspath)), "func.fixture" + ): + yield + + +@pytest.fixture(autouse=True) +async def check_for_pause(request, pytestconfig): + # When we unshare inline we can't pause in the pytest_runtest_makereport hook + # so do it here. + if BaseMunet.g_unet and BaseMunet.g_unet.unshare_inline: + pause = bool(pytestconfig.getoption("--pause")) + if pause: + await async_pause_test(f"XXX before test '{request.node.name}'") + yield + + +@pytest.fixture(scope="function") +def stepf(pytestconfig): + class Stepnum: + """Track the stepnum in closure.""" + + num = 0 + + def inc(self): + self.num += 1 + + pause = pytestconfig.getoption("pause") + stepnum = Stepnum() + + def stepfunction(desc=""): + desc = f": {desc}" if desc else "" + if pause: + pause_test(f"before step {stepnum.num}{desc}") + logging.info("STEP %s%s", stepnum.num, desc) + stepnum.inc() + + return stepfunction + + +@pytest_asyncio.fixture(scope="function") +async def astepf(pytestconfig): + class Stepnum: + """Track the stepnum in closure.""" + + num = 0 + + def inc(self): + self.num += 1 + + pause = pytestconfig.getoption("pause") + stepnum = Stepnum() + + async def stepfunction(desc=""): + desc = f": {desc}" if desc else "" + if pause: + await async_pause_test(f"before step {stepnum.num}{desc}") + logging.info("STEP %s%s", stepnum.num, desc) + stepnum.inc() + + return stepfunction + + +@pytest.fixture(scope="function") +def rundir(): + d = os.path.join("/tmp/unet-test", get_test_logdir(module=False)) + logging.debug("conftest: test function rundir %s", d) + return d + + +# Configure logging +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_setup(item): + d = os.path.join( + "/tmp/unet-test", get_test_logdir(nodeid=item.nodeid, module=False) + ) + config = item.config + logging_plugin = config.pluginmanager.get_plugin("logging-plugin") + filename = Path(d, "pytest-exec.log") + logging_plugin.set_log_path(str(filename)) + logging.debug("conftest: test function setup: rundir %s", d) + yield + + +@pytest.fixture +async def unet_perfunc(request, rundir, pytestconfig): # pylint: disable=W0621 + param = request.param if hasattr(request, "param") else None + async for x in _unet_impl(rundir, pytestconfig, param=param): + yield x + + +@pytest.fixture +async def unet_perfunc_unshare(request, rundir, pytestconfig): # pylint: disable=W0621 + """Build unet per test function with an optional topology basename parameter. + + The fixture can be parameterized to choose different config files. + For example, use as follows to run the test with unet_perfunc configured + first with a config file named `cfg1.yaml` then with config file `cfg2.yaml` + (where the actual files could end with `json` or `toml` rather than `yaml`). + + @pytest.mark.parametrize( + "unet_perfunc", ["cfg1", "cfg2]", indirect=["unet_perfunc"] + ) + def test_example(unet_perfunc) + """ + param = request.param if hasattr(request, "param") else None + async for x in _unet_impl(rundir, pytestconfig, unshare=True, param=param): + yield x + + +@pytest.fixture +async def unet_perfunc_share(request, rundir, pytestconfig): # pylint: disable=W0621 + """Build unet per test function with an optional topology basename parameter. + + This share variant keeps munet from unsharing the process to a new namespace so that + root level commands and actions are execute on the host, normally they are executed + in the munet namespace which allowing things like scapy inline in tests to work. + + The fixture can be parameterized to choose different config files. For example, use + as follows to run the test with unet_perfunc configured first with a config file + named `cfg1.yaml` then with config file `cfg2.yaml` (where the actual files could + end with `json` or `toml` rather than `yaml`). + + @pytest.mark.parametrize( + "unet_perfunc", ["cfg1", "cfg2]", indirect=["unet_perfunc"] + ) + def test_example(unet_perfunc) + """ + param = request.param if hasattr(request, "param") else None + async for x in _unet_impl(rundir, pytestconfig, unshare=False, param=param): + yield x diff --git a/tests/topotests/munet/testing/hooks.py b/tests/topotests/munet/testing/hooks.py new file mode 100644 index 0000000000..9b6a49a18c --- /dev/null +++ b/tests/topotests/munet/testing/hooks.py @@ -0,0 +1,225 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 22 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2022, LabN Consulting, L.L.C +# +"""A module that implements pytest hooks. + +To use in your project, in your conftest.py add: + + from munet.testing.hooks import * +""" +import logging +import os +import sys +import traceback + +import pytest + +from ..base import BaseMunet # pylint: disable=import-error +from ..cli import cli # pylint: disable=import-error +from .util import pause_test + + +# =================== +# Hooks (non-fixture) +# =================== + + +def pytest_addoption(parser): + parser.addoption( + "--cli-on-error", + action="store_true", + help="CLI on test failure", + ) + + parser.addoption( + "--coverage", + action="store_true", + help="Enable coverage gathering if supported", + ) + + parser.addoption( + "--gdb", + default="", + metavar="HOST[,HOST...]", + help="Comma-separated list of nodes to launch gdb on, or 'all'", + ) + parser.addoption( + "--gdb-breakpoints", + default="", + metavar="BREAKPOINT[,BREAKPOINT...]", + help="Comma-separated list of breakpoints", + ) + parser.addoption( + "--gdb-use-emacs", + action="store_true", + help="Use emacsclient to run gdb instead of a shell", + ) + + parser.addoption( + "--pcap", + default="", + metavar="NET[,NET...]", + help="Comma-separated list of networks to capture packets on, or 'all'", + ) + + parser.addoption( + "--pause", + action="store_true", + help="Pause after each test", + ) + parser.addoption( + "--pause-at-end", + action="store_true", + help="Pause before taking munet down", + ) + parser.addoption( + "--pause-on-error", + action="store_true", + help="Pause after (disables default when --shell or -vtysh given)", + ) + parser.addoption( + "--no-pause-on-error", + dest="pause_on_error", + action="store_false", + help="Do not pause after (disables default when --shell or -vtysh given)", + ) + + parser.addoption( + "--shell", + default="", + metavar="NODE[,NODE...]", + help="Comma-separated list of nodes to spawn shell on, or 'all'", + ) + + parser.addoption( + "--stdout", + default="", + metavar="NODE[,NODE...]", + help="Comma-separated list of nodes to open tail-f stdout window on, or 'all'", + ) + + parser.addoption( + "--stderr", + default="", + metavar="NODE[,NODE...]", + help="Comma-separated list of nodes to open tail-f stderr window on, or 'all'", + ) + + +def pytest_configure(config): + if "PYTEST_XDIST_WORKER" not in os.environ: + os.environ["PYTEST_XDIST_MODE"] = config.getoption("dist", "no") + os.environ["PYTEST_IS_WORKER"] = "" + is_xdist = os.environ["PYTEST_XDIST_MODE"] != "no" + is_worker = False + else: + os.environ["PYTEST_IS_WORKER"] = os.environ["PYTEST_XDIST_WORKER"] + is_xdist = True + is_worker = True + + # Turn on live logging if user specified verbose and the config has a CLI level set + if config.getoption("--verbose") and not is_xdist and not config.getini("log_cli"): + if config.getoption("--log-cli-level", None) is None: + # By setting the CLI option to the ini value it enables log_cli=1 + cli_level = config.getini("log_cli_level") + if cli_level is not None: + config.option.log_cli_level = cli_level + + have_tmux = bool(os.getenv("TMUX", "")) + have_screen = not have_tmux and bool(os.getenv("STY", "")) + have_xterm = not have_tmux and not have_screen and bool(os.getenv("DISPLAY", "")) + have_windows = have_tmux or have_screen or have_xterm + have_windows_pause = have_tmux or have_xterm + xdist_no_windows = is_xdist and not is_worker and not have_windows_pause + + for winopt in ["--shell", "--stdout", "--stderr"]: + b = config.getoption(winopt) + if b and xdist_no_windows: + pytest.exit( + f"{winopt} use requires byobu/TMUX/XTerm " + f"under dist {os.environ['PYTEST_XDIST_MODE']}" + ) + elif b and not is_xdist and not have_windows: + pytest.exit(f"{winopt} use requires byobu/TMUX/SCREEN/XTerm") + + +def pytest_runtest_makereport(item, call): + """Pause or invoke CLI as directed by config.""" + isatty = sys.stdout.isatty() + + pause = bool(item.config.getoption("--pause")) + skipped = False + + if call.excinfo is None: + error = False + elif call.excinfo.typename == "Skipped": + skipped = True + error = False + pause = False + else: + error = True + modname = item.parent.module.__name__ + exval = call.excinfo.value + logging.error( + "test %s/%s failed: %s: stdout: '%s' stderr: '%s'", + modname, + item.name, + exval, + exval.stdout if hasattr(exval, "stdout") else "NA", + exval.stderr if hasattr(exval, "stderr") else "NA", + ) + if not pause: + pause = item.config.getoption("--pause-on-error") + + if error and isatty and item.config.getoption("--cli-on-error"): + if not BaseMunet.g_unet: + logging.error("Could not launch CLI b/c no munet exists yet") + else: + print(f"\nCLI-ON-ERROR: {call.excinfo.typename}") + print(f"CLI-ON-ERROR:\ntest {modname}/{item.name} failed: {exval}") + if hasattr(exval, "stdout") and exval.stdout: + print("stdout: " + exval.stdout.replace("\n", "\nstdout: ")) + if hasattr(exval, "stderr") and exval.stderr: + print("stderr: " + exval.stderr.replace("\n", "\nstderr: ")) + cli(BaseMunet.g_unet) + + if pause: + if skipped: + item.skip_more_pause = True + elif hasattr(item, "skip_more_pause"): + pass + elif call.when == "setup": + if error: + item.skip_more_pause = True + + # we can't asyncio.run() (which pause does) if we are unhsare_inline + # at this point, count on an autouse fixture to pause instead in this + # case + if not BaseMunet.g_unet or not BaseMunet.g_unet.unshare_inline: + pause_test(f"before test '{item.nodeid}'") + + # check for a result to try and catch setup (or module setup) failure + # e.g., after a module level fixture fails, we do not want to pause on every + # skipped test. + elif call.when == "teardown" and call.excinfo: + logging.warning( + "Caught exception during teardown: %s\n:Traceback:\n%s", + call.excinfo, + "".join(traceback.format_tb(call.excinfo.tb)), + ) + pause_test(f"after teardown after test '{item.nodeid}'") + elif call.when == "teardown" and call.result: + pause_test(f"after test '{item.nodeid}'") + elif error: + item.skip_more_pause = True + print(f"\nPAUSE-ON-ERROR: {call.excinfo.typename}") + print(f"PAUSE-ON-ERROR:\ntest {modname}/{item.name} failed: {exval}") + if hasattr(exval, "stdout") and exval.stdout: + print("stdout: " + exval.stdout.replace("\n", "\nstdout: ")) + if hasattr(exval, "stderr") and exval.stderr: + print("stderr: " + exval.stderr.replace("\n", "\nstderr: ")) + pause_test(f"PAUSE-ON-ERROR: '{item.nodeid}'") diff --git a/tests/topotests/munet/testing/util.py b/tests/topotests/munet/testing/util.py new file mode 100644 index 0000000000..a1a94bcd1b --- /dev/null +++ b/tests/topotests/munet/testing/util.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 22 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2022, LabN Consulting, L.L.C +# +"""Utility functions useful when using munet testing functionailty in pytest.""" +import asyncio +import datetime +import functools +import logging +import sys +import time + +from ..base import BaseMunet +from ..cli import async_cli + + +# ================= +# Utility Functions +# ================= + + +async def async_pause_test(desc=""): + isatty = sys.stdout.isatty() + if not isatty: + desc = f" for {desc}" if desc else "" + logging.info("NO PAUSE on non-tty terminal%s", desc) + return + + while True: + if desc: + print(f"\n== PAUSING: {desc} ==") + try: + user = input('PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ') + except EOFError: + print("^D...continuing") + break + user = user.strip() + if user == "cli": + await async_cli(BaseMunet.g_unet) + elif user == "pdb": + breakpoint() # pylint: disable=W1515 + elif user: + print(f'Unrecognized input: "{user}"') + else: + break + + +def pause_test(desc=""): + asyncio.run(async_pause_test(desc)) + + +def retry(retry_timeout, initial_wait=0, expected=True): + """decorator: retry while functions return is not None or raises an exception. + + * `retry_timeout`: Retry for at least this many seconds; after waiting + initial_wait seconds + * `initial_wait`: Sleeps for this many seconds before first executing function + * `expected`: if False then the return logic is inverted, except for exceptions, + (i.e., a non None ends the retry loop, and returns that value) + """ + + def _retry(func): + @functools.wraps(func) + def func_retry(*args, **kwargs): + retry_sleep = 2 + + # Allow the wrapped function's args to override the fixtures + _retry_timeout = kwargs.pop("retry_timeout", retry_timeout) + _expected = kwargs.pop("expected", expected) + _initial_wait = kwargs.pop("initial_wait", initial_wait) + retry_until = datetime.datetime.now() + datetime.timedelta( + seconds=_retry_timeout + _initial_wait + ) + + if initial_wait > 0: + logging.info("Waiting for [%s]s as initial delay", initial_wait) + time.sleep(initial_wait) + + while True: + seconds_left = (retry_until - datetime.datetime.now()).total_seconds() + try: + ret = func(*args, **kwargs) + if _expected and ret is None: + logging.debug("Function succeeds") + return ret + logging.debug("Function returned %s", ret) + except Exception as error: + logging.info("Function raised exception: %s", str(error)) + ret = error + + if seconds_left < 0: + logging.info("Retry timeout of %ds reached", _retry_timeout) + if isinstance(ret, Exception): + raise ret + return ret + + logging.info( + "Sleeping %ds until next retry with %.1f retry time left", + retry_sleep, + seconds_left, + ) + time.sleep(retry_sleep) + + func_retry._original = func # pylint: disable=W0212 + return func_retry + + return _retry diff --git a/tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf index 6c7cb96240..e6e5733010 100644 --- a/tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf +++ b/tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf @@ -1,35 +1,35 @@ -debug ospf6 lsa all -debug ospf6 message all -debug ospf6 route all -debug ospf6 spf time -debug ospf6 spf database -debug ospf6 zebra send -debug ospf6 zebra recv - -debug ospf6 lsa router -debug ospf6 lsa router originate -debug ospf6 lsa router examine -debug ospf6 lsa router flooding -debug ospf6 lsa as-external -debug ospf6 lsa as-external originate -debug ospf6 lsa as-external examine -debug ospf6 lsa as-external flooding -debug ospf6 lsa intra-prefix -debug ospf6 lsa intra-prefix originate -debug ospf6 lsa intra-prefix examine -debug ospf6 lsa intra-prefix flooding -debug ospf6 border-routers -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 flooding -debug ospf6 gr helper -debug ospf6 spf process -debug ospf6 route intra-area -debug ospf6 route inter-area -debug ospf6 abr -debug ospf6 asbr -debug ospf6 nssa +!debug ospf6 lsa all +!debug ospf6 message all +!debug ospf6 route all +!debug ospf6 spf time +!debug ospf6 spf database +!debug ospf6 zebra send +!debug ospf6 zebra recv +! +!debug ospf6 lsa router +!debug ospf6 lsa router originate +!debug ospf6 lsa router examine +!debug ospf6 lsa router flooding +!debug ospf6 lsa as-external +!debug ospf6 lsa as-external originate +!debug ospf6 lsa as-external examine +!debug ospf6 lsa as-external flooding +!debug ospf6 lsa intra-prefix +!debug ospf6 lsa intra-prefix originate +!debug ospf6 lsa intra-prefix examine +!debug ospf6 lsa intra-prefix flooding +!debug ospf6 border-routers +!debug ospf6 zebra +!debug ospf6 interface +!debug ospf6 neighbor +!debug ospf6 flooding +!debug ospf6 gr helper +!debug ospf6 spf process +!debug ospf6 route intra-area +!debug ospf6 route inter-area +!debug ospf6 abr +!debug ospf6 asbr +!debug ospf6 nssa ! interface r1-eth0 ipv6 ospf6 area 0 diff --git a/tests/topotests/ospf_basic_functionality/ospf_lan.json b/tests/topotests/ospf_basic_functionality/ospf_lan.json index 126934c344..54863382b2 100644 --- a/tests/topotests/ospf_basic_functionality/ospf_lan.json +++ b/tests/topotests/ospf_basic_functionality/ospf_lan.json @@ -18,7 +18,7 @@ "ospf": { "area": "0.0.0.3", "hello_interval": 1, - "dead_interval": 4, + "dead_interval": 10, "priority": 98 } }, @@ -27,7 +27,7 @@ "ospf": { "area": "0.0.0.3", "hello_interval": 1, - "dead_interval": 4, + "dead_interval": 10, "priority": 99 } }, @@ -36,7 +36,7 @@ "ospf": { "area": "0.0.0.3", "hello_interval": 1, - "dead_interval": 4, + "dead_interval": 10, "priority": 0 } }, @@ -45,7 +45,7 @@ "ospf": { "area": "0.0.0.3", "hello_interval": 1, - "dead_interval": 4, + "dead_interval": 10, "priority": 0 } } @@ -135,4 +135,4 @@ } } } -}
\ No newline at end of file +} diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py index 3d15e94a51..9d7a15833c 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py @@ -140,7 +140,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # 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( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -260,11 +260,9 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf": { @@ -290,7 +288,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -306,7 +304,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Change the summary address mask to lower match (ex - 16 to 8)") ospf_summ_r1 = { @@ -334,7 +332,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step( "Verify that external routes(static / connected) are summarised" @@ -350,7 +348,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Change the summary address mask to higher match (ex - 8 to 24)") ospf_summ_r1 = { @@ -378,7 +376,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step( "Verify that external routes(static / connected) are summarised" @@ -399,7 +397,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step(" Un configure one of the summary address.") ospf_summ_r1 = { @@ -432,7 +430,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) ospf_summ_r1 = { "r0": { @@ -459,7 +457,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) write_test_footer(tc_name) @@ -506,11 +504,9 @@ def test_ospf_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { @@ -538,7 +534,7 @@ def test_ospf_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -554,26 +550,26 @@ def test_ospf_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step( "Configure route map and & rule to permit configured summary address," @@ -635,7 +631,7 @@ def test_ospf_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) input_dict = { SUMMARY["ipv4"][0]: { @@ -650,7 +646,7 @@ def test_ospf_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) write_test_footer(tc_name) @@ -697,7 +693,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step( "Configure External Route summary in R0 to summarise 5" @@ -731,7 +727,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -747,26 +743,26 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB" + "Error: Routes still present in RIB".format(tc_name) + ) step("Delete the configured summary") ospf_summ_r1 = { @@ -789,19 +785,17 @@ def test_ospf_type5_summary_tc42_p0(request): step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name + assert result is not True, ( + "Testcase {} : Failed \n Expected: Summary Route should not present in RIB" + "Error: Summary Route still present in RIB".format(tc_name) ) step("show ip ospf summary should not have any summary address.") @@ -816,9 +810,10 @@ def test_ospf_type5_summary_tc42_p0(request): } dut = "r0" result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Summary Route should not present in OSPF DB" + "Error: Summary still present in DB".format(tc_name) + ) dut = "r1" step("All 5 routes are advertised after deletion of configured summary.") @@ -829,7 +824,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("configure the summary again and delete static routes .") ospf_summ_r1 = { @@ -857,7 +852,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) input_dict = { "r0": { @@ -874,18 +869,18 @@ def test_ospf_type5_summary_tc42_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Add back static routes.") input_dict_static_rtes = { @@ -901,18 +896,18 @@ def test_ospf_type5_summary_tc42_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_static_rtes, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB" + "Error: Routes still present in RIB".format(tc_name) + ) input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} dut = "r1" @@ -923,7 +918,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show configure summaries.") @@ -940,7 +935,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Configure new static route which is matching configured summary.") input_dict_static_rtes = { @@ -1004,7 +999,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Shut one of the interface") intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"] @@ -1076,7 +1071,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} @@ -1087,7 +1082,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) ospf_summ_r1 = { "r0": { @@ -1113,18 +1108,18 @@ def test_ospf_type5_summary_tc42_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB" + "Error: Routes still present in RIB".format(tc_name) + ) ospf_summ_r1 = { "r0": { @@ -1188,11 +1183,9 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf": { @@ -1224,7 +1217,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1240,7 +1233,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary") ospf_summ_r1 = { @@ -1263,19 +1256,17 @@ def test_ospf_type5_summary_tc45_p0(request): step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name + assert result is not True, ( + "Testcase {} : Failed \n Expected: Summary Routes should not be present in RIB. \n" + "Error: Summary Route still present in RIB".format(tc_name) ) step("show ip ospf summary should not have any summary address.") @@ -1292,7 +1283,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary still present in DB".format(tc_name) step("Configure Min tag value") ospf_summ_r1 = { @@ -1317,7 +1308,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1333,7 +1324,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Configure Max Tag Value") ospf_summ_r1 = { @@ -1363,7 +1354,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1382,7 +1373,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("configure new static route with different tag.") input_dict_static_rtes_11 = { @@ -1403,10 +1394,9 @@ def test_ospf_type5_summary_tc45_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( @@ -1418,9 +1408,10 @@ def test_ospf_type5_summary_tc45_p0(request): tag="88888", expected=False, ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step( "Verify that boundary values tags are used for summary route" @@ -1439,7 +1430,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary address") ospf_summ_r1 = { @@ -1470,24 +1461,24 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that summary address is flushed from neighbor.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Configure summary first & then configure matching static route.") @@ -1589,7 +1580,7 @@ def test_ospf_type5_summary_tc45_p0(request): assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -1619,11 +1610,9 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf": { @@ -1655,7 +1644,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1691,19 +1680,17 @@ def test_ospf_type5_summary_tc45_p0(request): step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB " + "Error: Summary Route still present in RIB".format(tc_name) ) step("show ip ospf summary should not have any summary address.") @@ -1720,7 +1707,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary still present in DB".format(tc_name) step("Configure Min tag value") ospf_summ_r1 = { @@ -1745,7 +1732,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1761,7 +1748,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Configure Max Tag Value") ospf_summ_r1 = { @@ -1791,7 +1778,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1810,7 +1797,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("configure new static route with different tag.") input_dict_static_rtes_11 = { @@ -1831,10 +1818,9 @@ def test_ospf_type5_summary_tc45_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( @@ -1846,9 +1832,10 @@ def test_ospf_type5_summary_tc45_p0(request): tag="88888", expected=False, ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n" + "Error: Routes still present in RIB".format(tc_name) + ) step( "Verify that boundary values tags are used for summary route" @@ -1867,7 +1854,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary address") ospf_summ_r1 = { @@ -1898,24 +1885,24 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that summary address is flushed from neighbor.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB \n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Configure summary first & then configure matching static route.") @@ -1999,7 +1986,7 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step( "Configure External Route summary in R0 to summarise 5" @@ -2031,20 +2018,20 @@ def test_ospf_type5_summary_tc46_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB." + "Error: Routes still present in RIB".format(tc_name) + ) - step("Verify that show ip ospf summary should show the " "configured summaries.") + step("Verify that show ip ospf summary should show the configured summaries.") input_dict = { SUMMARY["ipv4"][0]: { "summaryAddress": SUMMARY["ipv4"][0], @@ -2055,7 +2042,7 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary") ospf_summ_r1 = { @@ -2080,10 +2067,9 @@ def test_ospf_type5_summary_tc46_p0(request): step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( @@ -2091,9 +2077,7 @@ def test_ospf_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed. Error: Summary Route still present in RIB".format(tc_name) step("show ip ospf summary should not have any summary address.") input_dict = { @@ -2109,7 +2093,7 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary still present in DB".format(tc_name) step("Reconfigure summary with no advertise.") ospf_summ_r1 = { @@ -2138,20 +2122,20 @@ def test_ospf_type5_summary_tc46_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB \n" + "Error: Routes still present in RIB".format(tc_name) + ) - step("Verify that show ip ospf summary should show the " "configured summaries.") + step("Verify that show ip ospf summary should show the configured summaries.") input_dict = { SUMMARY["ipv4"][0]: { "summaryAddress": SUMMARY["ipv4"][0], @@ -2162,7 +2146,7 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step( "Change summary address from no advertise to advertise " @@ -2211,7 +2195,7 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2227,26 +2211,26 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB" + "Error: Routes is present in RIB".format(tc_name) + ) write_test_footer(tc_name) @@ -2293,11 +2277,9 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { @@ -2325,7 +2307,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2341,26 +2323,26 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n \n Expected: Routes should not be present in OSPF RIB.\n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n" + "Error: Routes still present in RIB".format(tc_name) + ) step( "configure route map and add rule to permit configured static " @@ -2423,7 +2405,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) input_dict = { SUMMARY["ipv4"][0]: { @@ -2461,18 +2443,18 @@ def test_ospf_type5_summary_tc47_p0(request): step("Verify that advertised summary route is flushed from neighbor.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB\n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB.\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Delete the configured route map.") @@ -2511,7 +2493,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) input_dict = { SUMMARY["ipv4"][0]: { @@ -2526,7 +2508,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Reconfigure the route map with denying configure summary address.") @@ -2570,7 +2552,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Redistribute static/connected routes without route map.") @@ -2606,7 +2588,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) input_dict = { SUMMARY["ipv4"][0]: { @@ -2621,7 +2603,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step( "Configure rule to deny all the routes in route map and configure" @@ -2672,18 +2654,18 @@ def test_ospf_type5_summary_tc47_p0(request): step("Verify that no summary route is originated.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB" + "Error: Routes still present in RIB".format(tc_name) + ) routemaps = { "r0": { @@ -2773,7 +2755,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2789,7 +2771,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Change route map rule for 1 of the routes to deny.") # Create ip prefix list @@ -2822,7 +2804,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("add rule in route map to deny configured summary address.") # Create ip prefix list @@ -2855,7 +2837,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) write_test_footer(tc_name) @@ -2995,7 +2977,7 @@ def test_ospf_type5_summary_tc51_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) write_test_footer(tc_name) @@ -3042,11 +3024,9 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { @@ -3074,7 +3054,7 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -3090,26 +3070,26 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Reload the FRR router") # stop/start -> restart FRR router and verify @@ -3130,7 +3110,7 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -3146,26 +3126,26 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Kill OSPFd daemon on R0.") kill_router_daemons(tgen, "r0", ["ospfd"]) @@ -3176,7 +3156,7 @@ def test_ospf_type5_summary_tc49_p2(request): 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( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -3194,7 +3174,7 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -3210,26 +3190,26 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("restart zebrad") kill_router_daemons(tgen, "r0", ["zebra"]) @@ -3251,7 +3231,7 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -3267,26 +3247,26 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n" + "Error: Routes still present in RIB".format(tc_name) + ) write_test_footer(tc_name) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py index cff59c3a40..603aeadb85 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py @@ -132,7 +132,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # 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( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -235,11 +235,9 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r0 = { "r0": { @@ -259,9 +257,7 @@ def test_ospf_type5_summary_tc44_p0(request): "route is sent to R1." ) - step( - "Configure summary & redistribute static/connected route with " "metric type 2" - ) + step("Configure summary & redistribute static/connected route with metric type 2") input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][3]}]}} dut = "r1" @@ -272,7 +268,7 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -288,7 +284,7 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Learn type 7 lsa from neighbours") @@ -312,7 +308,7 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) ospf_summ_r0 = { "r0": { @@ -340,7 +336,7 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Verify that already originated summary is intact.") input_dict = { @@ -356,7 +352,7 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) dut = "r1" aggr_timer = {"r1": {"ospf": {"aggr_timer": 6}}} diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py index 40df0b2308..88219b8400 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py @@ -100,7 +100,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -166,7 +166,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): step("Verify that the neighbour is not FULL between R1 and R2.") dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -192,7 +192,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -223,7 +223,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): ospf_covergence = verify_ospf_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=10 ) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -245,7 +245,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -260,7 +260,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): "show ip ospf neighbor cmd." ) ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -274,7 +274,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -314,7 +314,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -361,7 +361,7 @@ def test_ospf_authentication_md5_tc29_p1(request): ospf_covergence = verify_ospf_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=6 ) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -393,7 +393,7 @@ def test_ospf_authentication_md5_tc29_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -426,7 +426,7 @@ def test_ospf_authentication_md5_tc29_p1(request): ospf_covergence = verify_ospf_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=10 ) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -454,7 +454,7 @@ def test_ospf_authentication_md5_tc29_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -469,7 +469,7 @@ def test_ospf_authentication_md5_tc29_p1(request): "show ip ospf neighbor cmd." ) ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -483,7 +483,7 @@ def test_ospf_authentication_md5_tc29_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -528,7 +528,7 @@ def test_ospf_authentication_md5_tc29_p1(request): dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -576,7 +576,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): ospf_covergence = verify_ospf_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=10 ) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -608,7 +608,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -655,7 +655,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -687,7 +687,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -720,7 +720,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -765,7 +765,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -810,7 +810,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py index 124b36a5fa..e58f081f96 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py @@ -111,7 +111,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -177,7 +177,7 @@ def test_ospf_chaos_tc31_p1(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -198,7 +198,7 @@ def test_ospf_chaos_tc31_p1(request): 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( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -208,7 +208,7 @@ def test_ospf_chaos_tc31_p1(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -217,7 +217,7 @@ def test_ospf_chaos_tc31_p1(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -227,7 +227,7 @@ def test_ospf_chaos_tc31_p1(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -250,7 +250,7 @@ def test_ospf_chaos_tc31_p1(request): 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( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -260,7 +260,7 @@ def test_ospf_chaos_tc31_p1(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -316,7 +316,7 @@ def test_ospf_chaos_tc32_p1(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -338,7 +338,7 @@ def test_ospf_chaos_tc32_p1(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -361,7 +361,7 @@ def test_ospf_chaos_tc32_p1(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -421,7 +421,7 @@ def test_ospf_chaos_tc34_p1(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -444,7 +444,7 @@ def test_ospf_chaos_tc34_p1(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -453,7 +453,7 @@ def test_ospf_chaos_tc34_p1(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -463,7 +463,7 @@ def test_ospf_chaos_tc34_p1(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -488,7 +488,7 @@ def test_ospf_chaos_tc34_p1(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py index d58e2503ed..aba313db9f 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py @@ -114,7 +114,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # 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( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -168,7 +168,7 @@ def test_ospf_ecmp_tc16_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -217,7 +217,7 @@ def test_ospf_ecmp_tc16_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -227,7 +227,7 @@ def test_ospf_ecmp_tc16_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -259,7 +259,7 @@ def test_ospf_ecmp_tc16_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -286,7 +286,7 @@ def test_ospf_ecmp_tc16_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -303,7 +303,7 @@ def test_ospf_ecmp_tc16_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -343,7 +343,7 @@ def test_ospf_ecmp_tc17_p0(request): step("Verify that OSPF is up with 2 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -394,7 +394,7 @@ def test_ospf_ecmp_tc17_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -411,7 +411,7 @@ def test_ospf_ecmp_tc17_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) 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 6a565571be..1eeb23e9f7 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py @@ -115,7 +115,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -169,7 +169,7 @@ def test_ospf_lan_ecmp_tc18_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -222,7 +222,7 @@ def test_ospf_lan_ecmp_tc18_p0(request): dut = "r0" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -231,7 +231,7 @@ def test_ospf_lan_ecmp_tc18_p0(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -261,7 +261,7 @@ def test_ospf_lan_ecmp_tc18_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -278,7 +278,7 @@ def test_ospf_lan_ecmp_tc18_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py index 53b1be6d71..1358027f21 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py @@ -114,7 +114,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -165,9 +165,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -185,9 +185,9 @@ def test_ospf_lan_tc1_p0(request): "r1": { "ospf": { "neighbors": { - "r0": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r0": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -197,7 +197,8 @@ def test_ospf_lan_tc1_p0(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step( - "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers." + "Configure DR priority 100 on R0 and clear ospf neighbors " + "on all the routers." ) input_dict = { @@ -223,9 +224,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -235,7 +236,8 @@ def test_ospf_lan_tc1_p0(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step( - "Configure DR pririty 150 on R0 and clear ospf neighbors " "on all the routers." + "Configure DR priority 150 on R0 and clear ospf neighbors " + "on all the routers." ) input_dict = { @@ -261,9 +263,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -297,9 +299,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "2-Way", "role": "DROther"}, - "r3": {"state": "2-Way", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "2-Way", "role": "DROther"}, + "r3": {"nbrState": "2-Way", "role": "DROther"}, } } } @@ -336,9 +338,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -355,7 +357,7 @@ def test_ospf_lan_tc1_p0(request): result = verify_ospf_neighbor(tgen, topo, dut, lan=True, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r0: OSPF neighbors-hip is up \n Error: {}".format( + ), "Testcase {} : Failed \n r0: OSPF neighbors-hip is up \n Error: {}".format( tc_name, result ) @@ -368,9 +370,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -423,9 +425,9 @@ def test_ospf_lan_tc1_p0(request): "r1": { "ospf": { "neighbors": { - "r0": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r0": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -449,9 +451,9 @@ def test_ospf_lan_tc1_p0(request): "r1": { "ospf": { "neighbors": { - "r0": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r0": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -491,7 +493,7 @@ def test_ospf_lan_tc2_p0(request): "s1": { "ospf": { "priority": 98, - "timerDeadSecs": 4, + "timerDeadSecs": 10, "area": "0.0.0.3", "mcastMemberOspfDesignatedRouters": True, "mcastMemberOspfAllRouters": True, diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py index 0c4697cc21..d669e21d4d 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py @@ -112,7 +112,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # 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( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -220,11 +220,11 @@ def test_ospf_learning_tc15_p0(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) - step("Change area 1 as non nssa area (on the fly changing area" " type on DUT).") + step("Change area 1 as non nssa area (on the fly changing area type on DUT).") for rtr in ["r1", "r2", "r3"]: input_dict = { diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py index 858412f1d3..4f797743e7 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py @@ -411,17 +411,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.1": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.2": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.3": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } @@ -434,17 +434,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.0": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.2": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.3": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } @@ -457,17 +457,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.0": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.1": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.3": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } @@ -480,17 +480,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.0": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.1": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.2": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py index dad6d915e8..c9f43cdfe4 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py @@ -127,7 +127,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # 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( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -201,9 +201,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): 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" - ) + step("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 = { @@ -294,7 +292,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -303,7 +301,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are present in fib \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are present in fib \n Error: {}".format( tc_name, result ) @@ -347,7 +345,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -356,7 +354,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -404,7 +402,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -413,7 +411,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -464,7 +462,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, retry_timeout=4, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -479,7 +477,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -499,7 +497,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -508,7 +506,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -523,7 +521,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -532,7 +530,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -553,7 +551,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -562,7 +560,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -861,7 +859,7 @@ def test_ospf_routemaps_functionality_tc24_p0(request): result = verify_prefix_lists(tgen, pfx_list) assert ( result is not True - ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format( + ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format( tc_name, result ) @@ -930,7 +928,7 @@ def test_ospf_routemaps_functionality_tc24_p0(request): result = verify_prefix_lists(tgen, pfx_list) assert ( result is not True - ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format( + ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format( tc_name, result ) @@ -1078,7 +1076,7 @@ def test_ospf_routemaps_functionality_tc25_p0(request): # 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) 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 63c421ec84..f0950a2db3 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py @@ -123,7 +123,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # 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( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -171,7 +171,7 @@ def test_ospf_redistribution_tc5_p0(request): step("Verify that OSPF neighbors are FULL.") ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -295,7 +295,7 @@ def test_ospf_redistribution_tc6_p0(request): step("Verify that OSPF neighbors are FULL.") ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -524,7 +524,7 @@ def test_ospf_redistribution_tc8_p1(request): step("Verify that OSPF neighbours are reset and forms new adjacencies.") # 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -558,7 +558,7 @@ def test_ospf_rfc2328_appendinxE_p0(request): step("Verify that OSPF neighbours are Full.") # 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) 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 39bbab42e7..757d6fb1d5 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py @@ -108,7 +108,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -358,7 +358,7 @@ def test_ospf_p2p_tc3_p0(request): # Api call verify whether BGP is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -440,7 +440,7 @@ def test_ospf_hello_tc10_p0(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -486,7 +486,7 @@ def test_ospf_hello_tc10_p0(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -532,7 +532,7 @@ def test_ospf_hello_tc10_p0(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -575,7 +575,7 @@ def test_ospf_hello_tc10_p0(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -597,7 +597,7 @@ def test_ospf_show_p1(request): reset_config_on_routers(tgen) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) dut = "r1" @@ -690,7 +690,7 @@ def test_ospf_dead_tc11_p0(request): 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 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": { @@ -714,11 +714,11 @@ def test_ospf_dead_tc11_p0(request): 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( + assert ospf_covergence is True, "Testcase 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": { @@ -755,7 +755,7 @@ def test_ospf_dead_tc11_p0(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -797,7 +797,7 @@ def test_ospf_dead_tc11_p0(request): 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( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -835,9 +835,7 @@ def test_ospf_dead_tc11_p0(request): result = create_interfaces_cfg(tgen, topo1) 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." - ) + 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" result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) @@ -883,18 +881,14 @@ def test_ospf_tc4_mtu_ignore_p0(request): clear_ospf(tgen, "r0") - step( - "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State." - ) + step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.") result = verify_ospf_neighbor(tgen, topo, expected=False) assert result is not True, ( "Testcase {} : Failed \n OSPF nbrs are Full " "instead of Exstart. Error: {}".format(tc_name, result) ) - step( - "Verify that configured MTU value is updated in the show ip " "ospf interface." - ) + step("Verify that configured MTU value is updated in the show ip ospf interface.") dut = "r0" input_dict = {"r0": {"links": {"r1": {"ospf": {"mtuBytes": 1200}}}}} @@ -951,9 +945,7 @@ def test_ospf_tc4_mtu_ignore_p0(request): clear_ospf(tgen, "r0") - step( - "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State." - ) + step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.") result = verify_ospf_neighbor(tgen, topo, expected=False) assert result is not True, ( "Testcase {} : Failed \n OSPF nbrs are Full " @@ -970,9 +962,7 @@ def test_ospf_tc4_mtu_ignore_p0(request): result = verify_ospf_neighbor(tgen, topo) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step( - "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0." - ) + step("Configure ospf interface with jumbo MTU (9216). Reset ospf neighbors on R0.") rtr0.run("ip link set {} mtu 9216".format(r0_r1_intf)) rtr1.run("ip link set {} mtu 9216".format(r1_r0_intf)) diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py index 1c26596230..79374281cb 100644 --- a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py +++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py @@ -119,7 +119,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -182,20 +182,20 @@ def test_ospf_gr_helper_tc1_p0(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) - step("Verify that GR helper route is disabled by default to the in" "the DUT.") + step("Verify that GR helper route is disabled by default to the in the DUT.") input_dict = { "helperSupport": "Disabled", "strictLsaCheck": "Enabled", - "restartSupoort": "Planned and Unplanned Restarts", + "restartSupport": "Planned and Unplanned Restarts", "supportedGracePeriod": 1800, } dut = "r0" result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("Verify that DUT does not enter helper mode upon receiving the " "grace lsa.") + step("Verify that DUT does not enter helper mode upon receiving the grace lsa.") # send grace lsa scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt) @@ -205,7 +205,7 @@ def test_ospf_gr_helper_tc1_p0(request): result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format( + ), "Testcase {} : Failed. DUT entered helper role \n Error: {}".format( tc_name, result ) @@ -220,7 +220,7 @@ def test_ospf_gr_helper_tc1_p0(request): input_dict = { "helperSupport": "Enabled", "strictLsaCheck": "Enabled", - "restartSupoort": "Planned and Unplanned Restarts", + "restartSupport": "Planned and Unplanned Restarts", "supportedGracePeriod": 1800, } dut = "r0" @@ -234,7 +234,7 @@ def test_ospf_gr_helper_tc1_p0(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("Perform GR in RR.") - step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.") + step("Verify that DUT does enter helper mode upon receiving the grace lsa.") input_dict = {"activeRestarterCnt": 1} gracelsa_sent = False repeat = 0 @@ -277,7 +277,7 @@ def test_ospf_gr_helper_tc1_p0(request): result = create_router_ospf(tgen, topo, ospf_gr_r0) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.") + step("Verify that DUT does enter helper mode upon receiving the grace lsa.") input_dict = {"activeRestarterCnt": 1} gracelsa_sent = False repeat = 0 @@ -306,7 +306,7 @@ def test_ospf_gr_helper_tc1_p0(request): result = create_router_ospf(tgen, topo, ospf_gr_r0) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("Verify that GR helper router is disabled in the DUT for" " router id x.x.x.x") + step("Verify that GR helper router is disabled in the DUT for router id x.x.x.x") input_dict = {"enabledRouterIds": [{"routerId": "1.1.1.1"}]} dut = "r0" result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False) @@ -343,7 +343,7 @@ def test_ospf_gr_helper_tc2_p0(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) ospf_gr_r0 = { "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}} } diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py index a3ccb58d38..46c0da309f 100644 --- a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py +++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py @@ -119,7 +119,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -188,10 +188,10 @@ def test_ospf_gr_helper_tc3_p1(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) - step( - "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers." - ) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) + + step("Configure DR priority 100 on R0 and clear ospf neighbors " + "on all the routers.") input_dict = { "r0": { @@ -216,9 +216,9 @@ def test_ospf_gr_helper_tc3_p1(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -282,10 +282,8 @@ def test_ospf_gr_helper_tc4_p1(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) - step( - "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers." - ) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) + step("Configure DR priority 0 on R0 and clear ospf neighbors on all the routers.") input_dict = { "r0": { @@ -310,9 +308,9 @@ def test_ospf_gr_helper_tc4_p1(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "2-Way", "role": "DROther"}, - "r3": {"state": "2-Way", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "2-Way", "role": "DROther"}, + "r3": {"nbrState": "2-Way", "role": "DROther"}, } } } diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py index 64aac2fba8..3be28196d8 100644 --- a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py +++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py @@ -119,7 +119,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -193,7 +193,7 @@ def test_ospf_gr_helper_tc7_p1(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) ospf_gr_r0 = { "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}} } @@ -221,7 +221,7 @@ def test_ospf_gr_helper_tc7_p1(request): result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format( + ), "Testcase {} : Failed. DUT entered helper role \n Error: {}".format( tc_name, result ) @@ -253,7 +253,7 @@ def test_ospf_gr_helper_tc8_p1(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) ospf_gr_r0 = { "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}} } diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json index f82758101c..0212f9da9c 100644 --- a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json @@ -3,7 +3,7 @@ "2.2.2.2":[ { "converged":"Full", - "address":"10.0.1.2", + "ifaceAddress":"10.0.1.2", "ifaceName":"eth-rt2:10.0.1.1" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json index 5a0b092702..3114660483 100644 --- a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json @@ -3,14 +3,14 @@ "1.1.1.1":[ { "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"eth-rt1:10.0.1.2" } ], "3.3.3.3":[ { "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"eth-rt3:10.0.2.2" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json index ab5e78414d..49a019d36d 100644 --- a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json @@ -3,21 +3,21 @@ "2.2.2.2":[ { "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"eth-rt2:10.0.2.3" } ], "4.4.4.4":[ { "converged":"Full", - "address":"10.0.3.4", + "ifaceAddress":"10.0.3.4", "ifaceName":"eth-rt4:10.0.3.3" } ], "6.6.6.6":[ { "converged":"Full", - "address":"10.0.4.6", + "ifaceAddress":"10.0.4.6", "ifaceName":"eth-rt6:10.0.4.3" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json index 405679c10e..9ab49d7266 100644 --- a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json @@ -3,14 +3,14 @@ "3.3.3.3":[ { "converged":"Full", - "address":"10.0.3.3", + "ifaceAddress":"10.0.3.3", "ifaceName":"eth-rt3:10.0.3.4" } ], "5.5.5.5":[ { "converged":"Full", - "address":"10.0.5.5", + "ifaceAddress":"10.0.5.5", "ifaceName":"eth-rt5:10.0.5.4" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json index 893d454368..7d3d589772 100644 --- a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json @@ -3,7 +3,7 @@ "4.4.4.4":[ { "converged":"Full", - "address":"10.0.5.4", + "ifaceAddress":"10.0.5.4", "ifaceName":"eth-rt4:10.0.5.5" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json index 564a513ac6..506eb4086b 100644 --- a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json @@ -3,14 +3,14 @@ "3.3.3.3":[ { "converged":"Full", - "address":"10.0.4.3", + "ifaceAddress":"10.0.4.3", "ifaceName":"eth-rt3:10.0.4.6" } ], "7.7.7.7":[ { "converged":"Full", - "address":"10.0.6.7", + "ifaceAddress":"10.0.6.7", "ifaceName":"eth-rt7:10.0.6.6" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json index bc6b60697c..6429148004 100644 --- a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json @@ -3,7 +3,7 @@ "6.6.6.6":[ { "converged":"Full", - "address":"10.0.6.6", + "ifaceAddress":"10.0.6.6", "ifaceName":"eth-rt6:10.0.6.7" } ] diff --git a/tests/topotests/ospf_metric_propagation/__init__.py b/tests/topotests/ospf_metric_propagation/__init__.py new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/__init__.py diff --git a/tests/topotests/ospf_metric_propagation/h1/frr.conf b/tests/topotests/ospf_metric_propagation/h1/frr.conf new file mode 100644 index 0000000000..1196a192dd --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/h1/frr.conf @@ -0,0 +1,10 @@ +! +hostname h1 +password zebra +log file /tmp/h1-frr.log +! +ip route 0.0.0.0/0 10.0.91.1 +! +interface h1-eth0 + ip address 10.0.91.2/24 +!
\ No newline at end of file diff --git a/tests/topotests/ospf_metric_propagation/h2/frr.conf b/tests/topotests/ospf_metric_propagation/h2/frr.conf new file mode 100644 index 0000000000..f951fe6ba1 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/h2/frr.conf @@ -0,0 +1,10 @@ +! +hostname h2 +password zebra +log file /tmp/h2-frr.log +! +ip route 0.0.0.0/0 10.0.94.4 +! +interface h2-eth0 + ip address 10.0.94.2/24 +!
\ No newline at end of file diff --git a/tests/topotests/ospf_metric_propagation/r1/frr.conf b/tests/topotests/ospf_metric_propagation/r1/frr.conf new file mode 100644 index 0000000000..85230494dd --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/frr.conf @@ -0,0 +1,96 @@ +! +hostname r1 +password zebra +log file /tmp/r1-frr.log +ip forwarding +! +interface r1-eth0 + ip address 10.0.1.1/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r1-eth1 vrf blue + ip address 10.0.10.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +interface r1-eth2 vrf green + ip address 10.0.91.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +router ospf + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.1.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.10.0/24 area 0 +! +router ospf vrf green + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.91.0/24 area 0 +! +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf route-map rmap + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf route-map rmap + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +ip prefix-list min seq 5 permit 10.0.80.0/24 +route-map costmax permit 20 + set metric-type type-1 + set metric +1 + set metric-min 713 + match ip address prefix-list min + exit +! +ip prefix-list max seq 10 permit 10.0.70.0/24 +route-map costplus permit 30 + set metric-type type-1 + set metric +1 + set metric-max 13 + match ip address prefix-list max + exit +! +route-map costplus permit 40 + set metric-type type-1 + set metric +1 + exit
\ No newline at end of file diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json new file mode 100644 index 0000000000..e3a5cc410f --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":34, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.10.5", + "afi":"ipv4", + "interfaceIndex":6, + "interfaceName":"r1-eth1", + "vrf":"blue", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json new file mode 100644 index 0000000000..f3597bf458 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":136, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":5, + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json new file mode 100644 index 0000000000..eebcab83e4 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":1138, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":5, + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json new file mode 100644 index 0000000000..d0e3d816d3 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":1218, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":5, + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json new file mode 100644 index 0000000000..989ccf7798 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":238, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":5, + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json new file mode 100644 index 0000000000..84b11886e4 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":136, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.10.5", + "afi":"ipv4", + "interfaceIndex":6, + "interfaceName":"r1-eth1", + "vrf":"blue", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r2/frr.conf b/tests/topotests/ospf_metric_propagation/r2/frr.conf new file mode 100644 index 0000000000..e67a374ff5 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r2/frr.conf @@ -0,0 +1,81 @@ +! +hostname r2 +password zebra +log file /tmp/r2-frr.log +ip forwarding +! +interface r2-eth0 + ip address 10.0.1.2/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r2-eth1 vrf blue + ip address 10.0.20.2/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r2-eth2 vrf green + ip address 10.0.70.2/24 + ip ospf cost 1000 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.2 + distance 20 + redistribute bgp route-map costplus + network 10.0.1.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.2 + distance 20 + redistribute bgp route-map costplus + network 10.0.20.0/24 area 0 +! + +router ospf vrf green + ospf router-id 10.0.255.2 + distance 20 + redistribute bgp route-map costplus + network 10.0.70.0/24 area 0 +! + +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +route-map costplus permit 1 + set metric-type type-1 + set metric +1 + exit diff --git a/tests/topotests/ospf_metric_propagation/r3/frr.conf b/tests/topotests/ospf_metric_propagation/r3/frr.conf new file mode 100644 index 0000000000..175851d427 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r3/frr.conf @@ -0,0 +1,79 @@ +! +hostname r3 +password zebra +log file /tmp/r3-frr.log +ip forwarding +! +interface r3-eth0 + ip address 10.0.3.3/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r3-eth1 vrf blue + ip address 10.0.30.3/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r3-eth2 vrf green + ip address 10.0.80.3/24 + ip ospf cost 1000 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.3 + distance 20 + redistribute bgp route-map costplus + network 10.0.3.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.3 + distance 20 + redistribute bgp route-map costplus + network 10.0.30.0/24 area 0 +! +router ospf vrf green + ospf router-id 10.0.255.3 + distance 20 + redistribute bgp route-map costplus + network 10.0.80.0/24 area 0 +! +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +route-map costplus permit 1 + set metric-type type-1 + set metric +1 + exit diff --git a/tests/topotests/ospf_metric_propagation/r4/frr.conf b/tests/topotests/ospf_metric_propagation/r4/frr.conf new file mode 100644 index 0000000000..70a47e34fa --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r4/frr.conf @@ -0,0 +1,78 @@ +! +hostname r4 +password zebra +log file /tmp/r4-frr.log +ip forwarding +! +interface r4-eth0 + ip address 10.0.3.4/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r4-eth1 vrf blue + ip address 10.0.40.4/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r4-eth2 vrf green + ip address 10.0.94.4/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.4 + distance 20 + redistribute bgp route-map costplus + network 10.0.3.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.4 + distance 20 + redistribute bgp route-map costplus + network 10.0.40.0/24 area 0 +! +router ospf vrf green + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.94.0/24 area 0 +! +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf route-map costplus + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +route-map costplus permit 1 + set metric-type type-1 + set metric +1 + exit diff --git a/tests/topotests/ospf_metric_propagation/ra/frr.conf b/tests/topotests/ospf_metric_propagation/ra/frr.conf new file mode 100644 index 0000000000..7be9e5c33e --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/ra/frr.conf @@ -0,0 +1,27 @@ +! +hostname ra +password zebra +log file /tmp/ra-frr.log +ip forwarding +! +interface ra-eth0 + ip address 10.0.50.5/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface ra-eth1 + ip address 10.0.10.5/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface ra-eth2 + ip address 10.0.20.5/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.5 + network 10.0.10.0/24 area 0 + network 10.0.20.0/24 area 0 + network 10.0.50.0/24 area 0 +! diff --git a/tests/topotests/ospf_metric_propagation/rb/frr.conf b/tests/topotests/ospf_metric_propagation/rb/frr.conf new file mode 100644 index 0000000000..a7dbf82278 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/rb/frr.conf @@ -0,0 +1,27 @@ +! +hostname rb +password zebra +log file /tmp/rb-frr.log +ip forwarding +! +interface rb-eth0 + ip address 10.0.50.6/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface rb-eth1 + ip address 10.0.30.6/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface rb-eth2 + ip address 10.0.40.6/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.6 + network 10.0.30.0/24 area 0 + network 10.0.40.0/24 area 0 + network 10.0.50.0/24 area 0 +! diff --git a/tests/topotests/ospf_metric_propagation/rc/frr.conf b/tests/topotests/ospf_metric_propagation/rc/frr.conf new file mode 100644 index 0000000000..f5a2ed7c4f --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/rc/frr.conf @@ -0,0 +1,21 @@ +! +hostname rc +password zebra +log file /tmp/rc-frr.log +ip forwarding +! +interface rc-eth0 + ip address 10.0.70.7/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface rc-eth1 + ip address 10.0.80.7/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.7 + network 10.0.70.0/24 area 0 + network 10.0.80.0/24 area 0 +! diff --git a/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py new file mode 100644 index 0000000000..4d78bd2372 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_metric_propagation.py +# +# Copyright (c) 2023 ATCorp +# Jafar Al-Gharaibeh +# + +import os +import sys +import json +from time import sleep +from functools import partial +import pytest + +# 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 + + +""" +test_ospf_metric_propagation.py: Test OSPF/BGP metric propagation +""" + +TOPOLOGY = """ + +-----+ +-----+ + eth1 | | eth0 | | eth2 + +-------------+ rA +---------------------------+ rB +---------------+ + | .5 | | .5 .6 | | .6 | + | +--+--+ 10.0.50.0/24 +--+--+ .6 | + | |.5 |.6 | + | eth2| eth1| | + 10.0.10.0/24 | | | + | 10.0.20.0/24 10.0.30.0/24 10.0.40.0/24 + |blue |blue |blue |blue + | | | | + eth1|.1 eth1|.2 eth1|.3 eth1|.4 + +-----+ +--+--+ +--+--+ +-----+ +-+---+ +-+---+ +------+ + | |eth0 eth2| | eth0 | |eth2 eth1| |eth2 eth3| | eth0 | |eth2 eth0| | + | h1 +----------+ R1 +----------+ R2 +-----------+ rC +----------+ R3 +------------+ R4 +---------+ h2 | + | | | | | | | | | | | | | | + +-----+.2 .1 +-----+.1 .2+-----+.2 .7 +-----+.7 .3+-----+.3 .4+-----+.4 .2+------+ + green green green green + + 10.0.91.0/24 10.0.1.0/24 10.0.70.0/24 10.0.80.0/24 10.0.3.0/24 10.0.94.0/24 +""" + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + tgen.add_router("ra") + tgen.add_router("rb") + tgen.add_router("rc") + tgen.add_router("h1") + tgen.add_router("h2") + + # Interconect router 1, 2 + switch = tgen.add_switch("s1-2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Interconect router 3, 4 + switch = tgen.add_switch("s3-4") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + # Interconect router a, b + switch = tgen.add_switch("sa-b") + switch.add_link(tgen.gears["ra"]) + switch.add_link(tgen.gears["rb"]) + + # Interconect router 1, a + switch = tgen.add_switch("s1-a") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ra"]) + + # Interconect router 2, a + switch = tgen.add_switch("s2-a") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["ra"]) + + # Interconect router 3, b + switch = tgen.add_switch("s3-b") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["rb"]) + + # Interconect router 4, b + switch = tgen.add_switch("s4-b") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["rb"]) + + # Interconect router 1, h1 + switch = tgen.add_switch("s1-h1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["h1"]) + + # Interconect router 4, h2 + switch = tgen.add_switch("s4-h2") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["h2"]) + + # Interconect router 2, c + switch = tgen.add_switch("s2-c") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["rc"]) + + # Interconect router 3, c + switch = tgen.add_switch("s3-c") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["rc"]) + + +def setup_module(mod): + logger.info("OSPF Metric Propagation:\n {}".format(TOPOLOGY)) + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + vrf_setup_cmds = [ + "ip link add name blue type vrf table 11", + "ip link set dev blue up", + "ip link add name green type vrf table 12", + "ip link set dev green up", + ] + + # Starting Routers + router_list = tgen.routers() + + # Create VRFs and bind to interfaces + for routern in range(1, 5): + for cmd in vrf_setup_cmds: + tgen.net["r{}".format(routern)].cmd(cmd) + for routern in range(1, 5): + tgen.net["r{}".format(routern)].cmd( + "ip link set dev r{}-eth1 vrf blue up".format(routern) + ) + tgen.net["r{}".format(routern)].cmd( + "ip link set dev r{}-eth2 vrf green up".format(routern) + ) + + for rname, router in router_list.items(): + logger.info("Loading router %s" % rname) + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + # Initialize all routers. + tgen.start_router() + for router in router_list.values(): + if router.has_version("<", "4.20"): + tgen.set_error("unsupported version") + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_all_links_up(): + "Test path R1 -> Ra -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r1 = tgen.gears["r1"] + json_file = "{}/r1/show_ip_route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_down(): + "Test path R1 -> R2 -> Ra -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r1"].cmd("ip link set dev r1-eth1 down") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-2.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_down(): + "Test path R1 -> R2 -> Rc -> R3 -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r2"].cmd("ip link set dev r2-eth1 down") + tgen.net["r2"].cmd("ip link set dev r2-eth2 down") + # ospf dead-interval is set to 30 seconds, wait 35 seconds to clear the neighbor + sleep(35) + tgen.net["r2"].cmd("ip link set dev r2-eth2 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-3.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_3_down(): + "Test path R1 -> R2 -> Rc -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r3"].cmd("ip link set dev r3-eth0 down") + tgen.net["r3"].cmd("ip link set dev r3-eth1 down") + # ospf dead-interval is set to 30 seconds, wait 35 seconds to clear the neighbor + sleep(35) + tgen.net["r3"].cmd("ip link set dev r3-eth0 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-4.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_3_4_down(): + "Test path R1 -> R2 -> Rc -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r4"].cmd("ip link set dev r4-eth1 down") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-4.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_4_down(): + "Test path R1 -> R2 -> Rc -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring link 3 back up + tgen.net["r3"].cmd("ip link set dev r3-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-4.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_4_down(): + "Test path R1 -> R2 -> Ra -> Rb -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring back link 2 up + tgen.net["r2"].cmd("ip link set dev r2-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-5.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_4_down(): + "Test path R1 -> Ra -> Rb -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring back link 1 up + tgen.net["r1"].cmd("ip link set dev r1-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-6.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_3_4_up(): + "Test path R1 -> Ra -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring back link 4 up + tgen.net["r4"].cmd("ip link set dev r4-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, 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/ospf_nssa_topo1/__init__.py b/tests/topotests/ospf_nssa_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/__init__.py diff --git a/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf new file mode 100644 index 0000000000..6d23c84806 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt1 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 0 +! +interface eth-rt2 + ip ospf network point-to-point + ip ospf area 0 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 1.1.1.1 +! diff --git a/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf new file mode 100644 index 0000000000..7ba3dc7591 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf @@ -0,0 +1,6 @@ +log file staticd.log +! +hostname rt1 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref new file mode 100644 index 0000000000..ac34417bda --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref new file mode 100644 index 0000000000..ac34417bda --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref new file mode 100644 index 0000000000..ac34417bda --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref new file mode 100644 index 0000000000..ac34417bda --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref new file mode 100644 index 0000000000..6a05555eec --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref new file mode 100644 index 0000000000..6a05555eec --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref new file mode 100644 index 0000000000..2d3c8c485f --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref @@ -0,0 +1,91 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref new file mode 100644 index 0000000000..6a05555eec --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref new file mode 100644 index 0000000000..f41ee3b698 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref new file mode 100644 index 0000000000..2d3c8c485f --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref @@ -0,0 +1,91 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf new file mode 100644 index 0000000000..1df100564e --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf @@ -0,0 +1,18 @@ +log file zebra.log +! +hostname rt1 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 +! +interface eth-rt2 + ip address 10.0.1.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf new file mode 100644 index 0000000000..12884d2ea9 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf @@ -0,0 +1,35 @@ +password 1 +hostname rt2 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 0 +! +interface eth-rt1 + ip ospf network point-to-point + ip ospf area 0 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +interface eth-rt3 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +interface eth-rt4 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 2.2.2.2 + area 1 nssa +! diff --git a/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf new file mode 100644 index 0000000000..b6d4233a4f --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf @@ -0,0 +1,6 @@ +log file staticd.log +! +hostname rt2 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c09411741a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c09411741a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref new file mode 100644 index 0000000000..a67dfb4685 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref @@ -0,0 +1,139 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "0.0.0.0\/0":{ + "routeType":"N E2", + "cost":10, + "type2cost":1, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c09411741a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c7dd93c6e9 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref @@ -0,0 +1,129 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref new file mode 100644 index 0000000000..9c3cfff6c0 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref @@ -0,0 +1,117 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref new file mode 100644 index 0000000000..f6bbdfa594 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c7dd93c6e9 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref @@ -0,0 +1,129 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c7dd93c6e9 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref @@ -0,0 +1,129 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c09411741a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf new file mode 100644 index 0000000000..fa274ebade --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf @@ -0,0 +1,24 @@ +log file zebra.log +! +hostname rt2 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 2.2.2.2/32 +! +interface eth-rt1 + ip address 10.0.1.2/24 +! +interface eth-rt3 + ip address 10.0.2.2/24 +! +interface eth-rt4 + ip address 10.0.3.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf new file mode 100644 index 0000000000..9691a7c512 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf @@ -0,0 +1,24 @@ +password 1 +hostname rt3 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 1 +! +interface eth-rt2 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 3.3.3.3 + area 1 nssa + redistribute connected +! diff --git a/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf new file mode 100644 index 0000000000..f0edd6c9ca --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf @@ -0,0 +1,8 @@ +log file staticd.log +! +hostname rt3 +! +ip route 0.0.0.0/0 Null0 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref new file mode 100644 index 0000000000..a2d078ab13 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref new file mode 100644 index 0000000000..4619067abd --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref new file mode 100644 index 0000000000..a2d078ab13 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref new file mode 100644 index 0000000000..a2d078ab13 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref new file mode 100644 index 0000000000..10387215b2 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref new file mode 100644 index 0000000000..4f8eaf1eaa --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref new file mode 100644 index 0000000000..41e9f6718a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref new file mode 100644 index 0000000000..10387215b2 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref new file mode 100644 index 0000000000..4619067abd --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref new file mode 100644 index 0000000000..4619067abd --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf new file mode 100644 index 0000000000..d943540f3b --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf @@ -0,0 +1,18 @@ +log file zebra.log +! +hostname rt3 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 3.3.3.3/32 +! +interface eth-rt2 + ip address 10.0.2.3/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf new file mode 100644 index 0000000000..cba7cf71ed --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf @@ -0,0 +1,24 @@ +password 1 +hostname rt4 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 1 +! +interface eth-rt2 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 4.4.4.4 + area 1 nssa + redistribute static +! diff --git a/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf new file mode 100644 index 0000000000..e00ee5dfea --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf @@ -0,0 +1,9 @@ +log file staticd.log +! +hostname rt4 +! +ip route 172.16.1.1/32 Null0 +ip route 172.16.1.2/32 Null0 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref new file mode 100644 index 0000000000..e57f542f1c --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref @@ -0,0 +1,114 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref new file mode 100644 index 0000000000..82a0e1a9b4 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref new file mode 100644 index 0000000000..e57f542f1c --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref @@ -0,0 +1,114 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref new file mode 100644 index 0000000000..e57f542f1c --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref @@ -0,0 +1,114 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref new file mode 100644 index 0000000000..5f51b3b3b8 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref new file mode 100644 index 0000000000..5f51b3b3b8 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref new file mode 100644 index 0000000000..5f51b3b3b8 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref new file mode 100644 index 0000000000..5f51b3b3b8 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref new file mode 100644 index 0000000000..82a0e1a9b4 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref new file mode 100644 index 0000000000..82a0e1a9b4 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf new file mode 100644 index 0000000000..588febe70b --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf @@ -0,0 +1,18 @@ +log file zebra.log +! +hostname rt4 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 4.4.4.4/32 +! +interface eth-rt2 + ip address 10.0.3.4/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py b/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py new file mode 100644 index 0000000000..432ddf0986 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py @@ -0,0 +1,416 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_nssa_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_ospf_nssa_topo1.py: + + +---------+ + | RT1 | + | 1.1.1.1 | + +---------+ + |eth-rt2 + | + |10.0.1.0/24 + | + |eth-rt1 + +---------+ + | RT2 | + | 2.2.2.2 | + +---------+ + eth-rt3| |eth-rt4 + | | + 10.0.2.0/24 | | 10.0.3.0/24 + +---------+ +--------+ + | | + |eth-rt2 |eth-rt2 + +---------+ +---------+ + | RT3 | | RT4 | + | 3.3.3.3 | | 4.4.4.4 | + +---------+ +---------+ + +""" + +import os +import sys +import pytest +import json +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. + +pytestmark = [pytest.mark.ospfd] + + +def build_topo(tgen): + "Build function" + + # + # Define FRR Routers + # + for router in ["rt1", "rt2", "rt3", "rt4"]: + 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["rt2"], nodeif="eth-rt4") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2") + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, 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_STATIC, os.path.join(CWD, "{}/staticd.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 print_cmd_result(rname, command): + print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False)) + + +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 +# +def test_rib_step1(): + logger.info("Test (step 1): test initial network convergence") + 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"]: + router_compare_json_output( + rname, "show ip ospf route json", "step1/show_ip_ospf_route.ref" + ) + + +# +# Step 2 +# +# Action(s): +# -rt3: configure an NSSA default route +# +# Expected changes: +# -rt2: add NSSA default route pointing to rt3 +# +def test_rib_step2(): + logger.info("Test (step 2): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Adding NSSA default on rt4") + tgen.net["rt3"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa default-information-originate"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step2/show_ip_ospf_route.ref" + ) + + +# +# Step 3 +# +# Action(s): +# -rt3: remove NSSA default route +# +# Expected changes: +# -rt2: remove NSSA default route +# +def test_rib_step3(): + logger.info("Test (step 3): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing NSSA default on rt4") + tgen.net["rt3"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step3/show_ip_ospf_route.ref" + ) + + +# +# Step 4 +# +# Action(s): +# -rt2: configure an NSSA range for 172.16.1.0/24 +# +# Expected changes: +# -rt1: the 172.16.1.1/32 and 172.16.1.2/32 routes should be removed +# -rt1: the 172.16.1.0/24 route should be added +# +def test_rib_step4(): + logger.info("Test (step 4): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring NSSA range on rt2") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step4/show_ip_ospf_route.ref" + ) + + +# +# Step 5 +# +# Action(s): +# -rt4: remove the 172.16.1.1/32 static route +# +# Expected changes: +# -None (the 172.16.1.0/24 range is still active because of 172.16.1.2/32) +# +def test_rib_step5(): + logger.info("Test (step 5): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing first static route in rt4") + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "no ip route 172.16.1.1/32 Null0"') + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step5/show_ip_ospf_route.ref" + ) + + +# +# Step 6 +# +# Action(s): +# -rt4: remove the 172.16.1.2/32 static route +# +# Expected changes: +# -rt1: remove the 172.16.1.0/24 route since the NSSA range is no longer active +# +def test_rib_step6(): + logger.info("Test (step 6): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing second static route in rt4") + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "no ip route 172.16.1.2/32 Null0"') + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step6/show_ip_ospf_route.ref" + ) + + +# +# Step 7 +# +# Action(s): +# -rt4: readd the 172.16.1.1/32 and 172.16.1.2/32 static routes +# +# Expected changes: +# -rt1: readd the 172.16.1.0/24 route since the NSSA range is active again +# +def test_rib_step7(): + logger.info("Test (step 7): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Readding static routes in rt4") + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "ip route 172.16.1.1/32 Null0"') + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "ip route 172.16.1.2/32 Null0"') + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step7/show_ip_ospf_route.ref" + ) + + +# +# Step 8 +# +# Action(s): +# -rt2: update the NSSA range with a static cost +# +# Expected changes: +# -rt1: update the metric of the 172.16.1.0/24 route from 20 to 1000 +# +def test_rib_step8(): + logger.info("Test (step 8): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Updating the NSSA range cost on rt2") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24 cost 1000"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step8/show_ip_ospf_route.ref" + ) + + +# +# Step 9 +# +# Action(s): +# -rt2: update the NSSA range to not advertise itself +# +# Expected changes: +# -rt1: the 172.16.1.0/24 route should be removed +# +def test_rib_step9(): + logger.info("Test (step 9): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Updating the NSSA range to not advertise itself") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24 not-advertise"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step9/show_ip_ospf_route.ref" + ) + + +# +# Step 10 +# +# Action(s): +# -rt2: remove the NSSA range +# +# Expected changes: +# -rt1: the 172.16.1.1/32 and 172.16.1.2/32 routes should be added +# +def test_rib_step10(): + logger.info("Test (step 10): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing NSSA range on rt2") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "no area 1 nssa range 172.16.1.0/24"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step10/show_ip_ospf_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/ospf_suppress_fa/test_ospf_suppress_fa.py b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py index 5b1a53b895..d5583ac06a 100644 --- a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py +++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py @@ -111,9 +111,7 @@ def ospf_unconfigure_suppress_fa(router_name, area): tgen = get_topogen() router = tgen.gears[router_name] - router.vtysh_cmd( - "conf t\nrouter ospf\nno area {} nssa suppress-fa\nexit\n".format(area) - ) + router.vtysh_cmd("conf t\nrouter ospf\narea {} nssa\nexit\n".format(area)) def ospf_get_lsa_type5(router_name): diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step1.json b/tests/topotests/ospf_te_topo1/reference/ted_step1.json index 18aee4ffab..8b2413a894 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step1.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step1.json @@ -57,7 +57,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -101,7 +101,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -142,7 +142,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -183,7 +183,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -224,7 +224,7 @@ } }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -266,7 +266,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -307,7 +307,7 @@ } }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -360,7 +360,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -404,7 +404,7 @@ } }, { - "edge-id":167773441, + "edge-id":"10.0.5.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step2.json b/tests/topotests/ospf_te_topo1/reference/ted_step2.json index 1ed7272560..625b57d15c 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step2.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step2.json @@ -57,7 +57,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -101,7 +101,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -142,7 +142,7 @@ } }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -184,7 +184,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -225,7 +225,7 @@ } }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -278,7 +278,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -322,7 +322,7 @@ } }, { - "edge-id":167773441, + "edge-id":"10.0.5.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step3.json b/tests/topotests/ospf_te_topo1/reference/ted_step3.json index 0e79670c78..4cfec0f608 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step3.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step3.json @@ -49,7 +49,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -93,7 +93,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -134,7 +134,7 @@ } }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -176,7 +176,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -217,7 +217,7 @@ } }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -270,7 +270,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step4.json b/tests/topotests/ospf_te_topo1/reference/ted_step4.json index 860dcb3f21..e8e24d9805 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step4.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step4.json @@ -72,7 +72,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -128,7 +128,7 @@ ] }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -181,7 +181,7 @@ ] }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -223,7 +223,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -276,7 +276,7 @@ ] }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -329,7 +329,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step5.json b/tests/topotests/ospf_te_topo1/reference/ted_step5.json index 615a691c45..4713cc0115 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step5.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step5.json @@ -72,7 +72,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -128,7 +128,7 @@ ] }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -181,7 +181,7 @@ ] }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -234,7 +234,7 @@ ] }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -287,7 +287,7 @@ ] }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -329,7 +329,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -382,7 +382,7 @@ ] }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -435,7 +435,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step6.json b/tests/topotests/ospf_te_topo1/reference/ted_step6.json index 3b84d25808..aaac07b051 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step6.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step6.json @@ -72,7 +72,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -128,7 +128,7 @@ ] }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -181,7 +181,7 @@ ] }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -234,7 +234,7 @@ ] }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -287,7 +287,7 @@ ] }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -329,7 +329,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -382,7 +382,7 @@ ] }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -437,7 +437,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step7.json b/tests/topotests/ospf_te_topo1/reference/ted_step7.json index 83f8a1d5d6..56ed1f176b 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step7.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step7.json @@ -53,7 +53,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -109,7 +109,7 @@ ] }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -162,7 +162,7 @@ ] }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -215,7 +215,7 @@ ] }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -268,7 +268,7 @@ ] }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -310,7 +310,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py index 55166367c1..b0e56e619a 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py @@ -156,7 +156,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -280,7 +280,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step( "Configure External Route summary in R0 to summarise 5" @@ -314,7 +314,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -330,9 +330,9 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]} } @@ -340,7 +340,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -349,7 +349,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step("Delete the configured summary") ospf_summ_r1 = { @@ -374,7 +374,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -383,9 +383,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name) step("show ip ospf summary should not have any summary address.") input_dict = { @@ -403,7 +401,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name) dut = "r1" step("All 5 routes are advertised after deletion of configured summary.") @@ -414,7 +412,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("configure the summary again and delete static routes .") ospf_summ_r1 = { @@ -442,7 +440,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) input_dict = { "r0": { @@ -461,7 +459,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -470,7 +468,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step("Add back static routes.") input_dict_static_rtes = { @@ -488,7 +486,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -497,7 +495,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}} dut = "r1" @@ -508,7 +506,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show configure summaries.") @@ -525,7 +523,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Configure new static route which is matching configured summary.") input_dict_static_rtes = { @@ -591,7 +589,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Shut one of the interface") intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"] @@ -663,7 +661,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}} @@ -674,7 +672,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) ospf_summ_r1 = { "r0": { @@ -702,7 +700,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -711,7 +709,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) ospf_summ_r1 = { "r0": { @@ -774,11 +772,9 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf6": { @@ -804,7 +800,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -820,7 +816,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Change the summary address mask to lower match (ex - 16 to 8)") ospf_summ_r1 = { @@ -855,7 +851,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step( "Verify that external routes(static / connected) are summarised" @@ -871,7 +867,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Change the summary address mask to higher match (ex - 8 to 24)") ospf_summ_r1 = { @@ -899,7 +895,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step( "Verify that external routes(static / connected) are summarised" @@ -920,7 +916,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step(" Un configure one of the summary address.") ospf_summ_r1 = { @@ -955,7 +951,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) ospf_summ_r1 = { "r0": { @@ -982,7 +978,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) write_test_footer(tc_name) @@ -1030,11 +1026,9 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf6": { @@ -1066,7 +1060,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1082,7 +1076,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary") ospf_summ_r1 = { @@ -1107,7 +1101,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1116,9 +1110,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name) step("show ip ospf summary should not have any summary address.") input_dict = { @@ -1136,7 +1128,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name) step("Configure Min tag value") ospf_summ_r1 = { @@ -1161,7 +1153,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1177,7 +1169,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Configure Max Tag Value") ospf_summ_r1 = { @@ -1207,7 +1199,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1226,7 +1218,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("configure new static route with different tag.") input_dict_static_rtes_11 = { @@ -1251,7 +1243,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1266,7 +1258,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1287,7 +1279,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary address") ospf_summ_r1 = { @@ -1318,7 +1310,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that summary address is flushed from neighbor.") @@ -1326,7 +1318,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1335,7 +1327,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step("Configure summary first & then configure matching static route.") @@ -1467,11 +1459,9 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf6": { @@ -1503,7 +1493,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1541,7 +1531,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1550,9 +1540,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name) step("show ip ospf summary should not have any summary address.") input_dict = { @@ -1570,7 +1558,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name) step("Configure Min tag value") ospf_summ_r1 = { @@ -1595,7 +1583,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1611,7 +1599,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Configure Max Tag Value") ospf_summ_r1 = { @@ -1641,7 +1629,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1660,7 +1648,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("configure new static route with different tag.") input_dict_static_rtes_11 = { @@ -1685,7 +1673,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1700,7 +1688,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1721,7 +1709,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary address") ospf_summ_r1 = { @@ -1752,7 +1740,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that summary address is flushed from neighbor.") @@ -1760,7 +1748,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1769,7 +1757,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step("Configure summary first & then configure matching static route.") @@ -1853,7 +1841,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step( "Configure External Route summary in R0 to summarise 5" @@ -1887,7 +1875,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1896,9 +1884,9 @@ def test_ospfv3_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) - step("Verify that show ip ospf summary should show the " "configured summaries.") + step("Verify that show ip ospf summary should show the configured summaries.") input_dict = { SUMMARY["ipv6"][0]: { "summaryAddress": SUMMARY["ipv6"][0], @@ -1909,7 +1897,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary") ospf_summ_r1 = { @@ -1936,7 +1924,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1945,9 +1933,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name) step("show ip ospf summary should not have any summary address.") input_dict = { @@ -1965,7 +1951,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name) step("Reconfigure summary with no advertise.") ospf_summ_r1 = { @@ -1996,7 +1982,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2005,9 +1991,9 @@ def test_ospfv3_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) - step("Verify that show ip ospf summary should show the " "configured summaries.") + step("Verify that show ip ospf summary should show the configured summaries.") input_dict = { SUMMARY["ipv6"][0]: { "summaryAddress": SUMMARY["ipv6"][0], @@ -2018,7 +2004,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step( "Change summary address from no advertise to advertise " @@ -2067,7 +2053,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2083,9 +2069,9 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") output = tgen.gears["r0"].vtysh_cmd( "show ipv6 ospf6 database as-external json", isjson=True ) @@ -2101,7 +2087,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2110,7 +2096,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is present in RIB".format(tc_name) write_test_footer(tc_name) @@ -2157,11 +2143,9 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { @@ -2189,7 +2173,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2205,9 +2189,9 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]} } @@ -2215,7 +2199,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2224,7 +2208,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step( "Configure route map and & rule to permit configured summary address," @@ -2287,7 +2271,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) input_dict = { SUMMARY["ipv6"][0]: { @@ -2302,7 +2286,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Configure metric type as 1 in route map.") @@ -2339,7 +2323,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Un configure metric type from route map.") @@ -2376,7 +2360,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Change rule from permit to deny in prefix list.") pfx_list = { @@ -2407,7 +2391,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2416,7 +2400,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) write_test_footer(tc_name) @@ -2556,7 +2540,7 @@ def test_ospfv3_type5_summary_tc51_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) write_test_footer(tc_name) @@ -2603,11 +2587,9 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { @@ -2635,7 +2617,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2651,9 +2633,9 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]} } @@ -2661,7 +2643,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2670,7 +2652,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step("Reload the FRR router") # stop/start -> restart FRR router and verify @@ -2691,7 +2673,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2707,9 +2689,9 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]} } @@ -2717,7 +2699,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2726,7 +2708,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) write_test_footer(tc_name) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py index 2f90c98785..58608e249b 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py @@ -111,7 +111,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf6_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf6_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "setup_module :Failed \n Error: {}".format( ospf6_covergence ) @@ -182,7 +182,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -214,7 +214,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -244,7 +244,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=5 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -272,7 +272,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -287,7 +287,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): "show ip ospf6 neighbor cmd." ) ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -301,7 +301,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -348,7 +348,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -380,7 +380,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -410,7 +410,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=5 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -438,7 +438,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -453,7 +453,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): "show ip ospf6 neighbor cmd." ) ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -467,7 +467,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -531,7 +531,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -561,7 +561,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -580,7 +580,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=5 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -606,7 +606,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -621,7 +621,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): "show ip ospf6 neighbor cmd." ) ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -635,7 +635,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -699,7 +699,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -729,7 +729,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -748,7 +748,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=5 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -774,7 +774,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -789,7 +789,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): "show ip ospf6 neighbor cmd." ) ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -803,7 +803,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -850,7 +850,7 @@ def test_ospf6_auth_trailer_tc5_md5_keymissmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -887,7 +887,7 @@ def test_ospf6_auth_trailer_tc5_md5_keymissmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -919,7 +919,7 @@ def test_ospf6_auth_trailer_tc5_md5_keymissmatch(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -966,7 +966,7 @@ def test_ospf6_auth_trailer_tc6_sha256_mismatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -998,7 +998,7 @@ def test_ospf6_auth_trailer_tc6_sha256_mismatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1030,7 +1030,7 @@ def test_ospf6_auth_trailer_tc6_sha256_mismatch(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1102,7 +1102,7 @@ def test_ospf6_auth_trailer_tc7_keychain_md5_missmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1132,7 +1132,7 @@ def test_ospf6_auth_trailer_tc7_keychain_md5_missmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1162,7 +1162,7 @@ def test_ospf6_auth_trailer_tc7_keychain_md5_missmatch(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1234,7 +1234,7 @@ def test_ospf6_auth_trailer_tc8_keychain_sha256_missmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1264,7 +1264,7 @@ def test_ospf6_auth_trailer_tc8_keychain_sha256_missmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1294,7 +1294,7 @@ def test_ospf6_auth_trailer_tc8_keychain_sha256_missmatch(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1342,7 +1342,7 @@ def test_ospf6_auth_trailer_tc9_keychain_not_configured(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1372,7 +1372,7 @@ def test_ospf6_auth_trailer_tc9_keychain_not_configured(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1402,7 +1402,7 @@ def test_ospf6_auth_trailer_tc10_no_auth_trailer(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py index 472364e84f..0c1e3fa43e 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py @@ -115,7 +115,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -257,7 +257,7 @@ def test_ospfv3_ecmp_tc16_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -352,7 +352,7 @@ def test_ospfv3_ecmp_tc16_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -390,7 +390,7 @@ def test_ospfv3_ecmp_tc17_p0(request): step("Verify that OSPF is up with 2 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py index 6b3e16965c..7c6773260e 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py @@ -128,7 +128,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf6_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -282,7 +282,7 @@ def test_ospfv3_lan_ecmp_tc18_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") ospf_covergence = verify_ospf6_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -343,7 +343,7 @@ def test_ospfv3_lan_ecmp_tc18_p0(request): dut = "r0" ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -352,7 +352,7 @@ def test_ospfv3_lan_ecmp_tc18_p0(request): dut = "r2" ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py index c0d8d718cc..dc4ce88830 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py @@ -73,7 +73,7 @@ def setup_module(mod): pytest.skip(tgen.errors) result = verify_ospf6_neighbor(tgen, topo) - assert result is True, "setup_module: Failed \n Error:" " {}".format(result) + assert result is True, "setup_module: Failed \n Error: {}".format(result) logger.info("Running setup_module() done") diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py index 138112775f..90548fb5ce 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py @@ -138,7 +138,7 @@ def setup_module(mod): # Api call verify whether OSPF is converged ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -277,7 +277,7 @@ def test_ospfv3_nssa_tc26_p0(request): result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result) + ), "Testcase {} : Failed \n Nbrs are not down Error: {}".format(tc_name, result) step("Now configure area 0 on interface of r1 connecting to r2.") @@ -349,7 +349,7 @@ def test_ospfv3_nssa_tc26_p0(request): result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result) + ), "Testcase {} : Failed \n Nbrs are not down Error: {}".format(tc_name, result) step("Now configure area 2 on interface of r1 connecting to r2.") @@ -471,7 +471,7 @@ def test_ospfv3_learning_tc15_p0(request): result = verify_ospf6_neighbor(tgen, topo) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("Change area 1 as non nssa area (on the fly changing area" " type on DUT).") + step("Change area 1 as non nssa area (on the fly changing area type on DUT).") for rtr in ["r1", "r2", "r3"]: input_dict = { diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py index b2cd0da24e..069806a3ef 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py @@ -129,7 +129,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -212,9 +212,7 @@ def test_ospfv3_routemaps_functionality_tc19_p0(request): result = create_router_ospf(tgen, topo, ospf_red_r1) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step( - "Create prefix-list in R0 to permit 10.0.20.1/32 prefix &" " deny 10.0.20.2/32" - ) + step("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 = { @@ -686,7 +684,7 @@ def test_ospfv3_routemaps_functionality_tc25_p0(request): # Api call verify whether OSPF is converged ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -1078,7 +1076,7 @@ def test_ospfv3_routemaps_functionality_tc24_p0(request): result = verify_prefix_lists(tgen, pfx_list) assert ( result is not True - ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format( + ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format( tc_name, result ) @@ -1147,7 +1145,7 @@ def test_ospfv3_routemaps_functionality_tc24_p0(request): result = verify_prefix_lists(tgen, pfx_list) assert ( result is not True - ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format( + ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format( tc_name, result ) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py index 9e7f112efb..645dea8dec 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py @@ -120,7 +120,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -256,7 +256,7 @@ def test_ospfv3_redistribution_tc5_p0(request): step("Verify that OSPF neighbors are FULL.") ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -616,7 +616,7 @@ def test_ospfv3_redistribution_tc8_p1(request): step("Verify that OSPF neighbours are reset and forms new adjacencies.") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py index 5d5ba1480f..7199f160fe 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py @@ -114,7 +114,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -460,7 +460,7 @@ def test_ospfv3_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -513,7 +513,7 @@ def test_ospfv3_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -566,7 +566,7 @@ def test_ospfv3_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -618,7 +618,7 @@ def test_ospfv3_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -709,9 +709,7 @@ def test_ospfv3_hello_tc10_p0(request): result = create_interfaces_cfg(tgen, topo1) 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." - ) + step("Verify that timer value is deleted from intf & set to default value 40 sec.") input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigHello": 10}}}}} dut = "r1" result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict) @@ -763,7 +761,7 @@ def test_ospfv3_dead_tc11_p0(request): result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict) 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": { @@ -799,7 +797,7 @@ def test_ospfv3_dead_tc11_p0(request): # reconfiguring deleted ospf process by resetting the configs. reset_config_on_routers(tgen) - 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": { @@ -920,9 +918,7 @@ def test_ospfv3_dead_tc11_p0(request): result = create_interfaces_cfg(tgen, topo1) 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." - ) + step("Verify that timer value is deleted from intf & set to default value 40 sec.") input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigDead": 40}}}}} dut = "r1" result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict) @@ -967,18 +963,14 @@ def test_ospfv3_tc4_mtu_ignore_p0(request): clear_ospf(tgen, "r0", ospf="ospf6") clear_ospf(tgen, "r1", ospf="ospf6") - step( - "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State." - ) + step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.") result = verify_ospf6_neighbor(tgen, topo, expected=False) assert result is not True, ( "Testcase {} : Failed \n OSPF nbrs are Full " "instead of Exstart. Error: {}".format(tc_name, result) ) - step( - "Verify that configured MTU value is updated in the show ip " "ospf interface." - ) + step("Verify that configured MTU value is updated in the show ip ospf interface.") dut = "r0" input_dict = {"r0": {"links": {"r1": {"ospf6": {"interfaceMtu": 1400}}}}} @@ -1035,9 +1027,7 @@ def test_ospfv3_tc4_mtu_ignore_p0(request): clear_ospf(tgen, "r0", ospf="ospf6") - step( - "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State." - ) + step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.") result = verify_ospf6_neighbor(tgen, topo, expected=False) assert result is not True, ( "Testcase {} : Failed \n OSPF nbrs are Full " @@ -1054,9 +1044,7 @@ def test_ospfv3_tc4_mtu_ignore_p0(request): result = verify_ospf6_neighbor(tgen, topo) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step( - "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0." - ) + step("Configure ospf interface with jumbo MTU (9216). Reset ospf neighbors on R0.") rtr0.run("ifconfig {} mtu 9216".format(r0_r1_intf)) rtr1.run("ifconfig {} mtu 9216".format(r1_r0_intf)) @@ -1091,53 +1079,6 @@ def test_ospfv3_show_p1(request): result = create_debug_log_config(tgen, input_dict) - # Code coverage steps #Do Not upstream - input_dict_config = { - "r1": { - "raw_config": [ - "end", - "debug ospf6 event", - "debug ospf6 gr helper", - "debug ospf6 ism events", - "debug ospf6 ism status", - "debug ospf6 ism timers", - "debug ospf6 nsm events", - "debug ospf6 nsm status", - "debug ospf6 nsm timers ", - "debug ospf6 nssa", - "debug ospf6 lsa aggregate", - "debug ospf6 lsa flooding ", - "debug ospf6 lsa generate", - "debug ospf6 lsa install ", - "debug ospf6 lsa refresh", - "debug ospf6 packet all detail", - "debug ospf6 packet all recv", - "debug ospf6 packet all send", - "debug ospf6 packet dd detail", - "debug ospf6 packet dd recv", - "debug ospf6 packet dd send ", - "debug ospf6 packet hello detail", - "debug ospf6 packet hello recv", - "debug ospf6 packet hello send", - "debug ospf6 packet ls-ack detail", - "debug ospf6 packet ls-ack recv", - "debug ospf6 packet ls-ack send", - "debug ospf6 packet ls-request detail", - "debug ospf6 packet ls-request recv", - "debug ospf6 packet ls-request send", - "debug ospf6 packet ls-update detail", - "debug ospf6 packet ls-update recv", - "debug ospf6 packet ls-update send", - "debug ospf6 sr", - "debug ospf6 te ", - "debug ospf6 zebra interface", - "debug ospf6 zebra redistribute", - ] - } - } - - apply_raw_config(tgen, input_dict_config) - for rtr in topo["routers"]: clear_ospf(tgen, rtr, ospf="ospf6") @@ -1234,7 +1175,7 @@ def ospfv3_router_id_tc14_p2(request): clear_ospf(tgen, rtr, ospf="ospf6") ospf_covergence = verify_ospf6_neighbor(tgen, topo1) - assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format( + assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error: {}".format( ospf_covergence ) @@ -1260,9 +1201,9 @@ def ospfv3_router_id_tc14_p2(request): assert result is True, "Testcase : Failed \n Error: {}".format(result) ospf_covergence = verify_ospf6_neighbor(tgen, topo, expected=False) - assert ( - ospf_covergence is not True - ), "OSPF NBRs are up.Failed \n Error:" " {}".format(ospf_covergence) + assert ospf_covergence is not True, "OSPF NBRs are up.Failed \n Error: {}".format( + ospf_covergence + ) topo1 = {} topo1 = deepcopy(topo) @@ -1305,7 +1246,7 @@ def ospfv3_router_id_tc14_p2(request): topo1["routers"]["r3"]["ospf6"]["router_id"] = "1.1.1.4" ospf_covergence = verify_ospf6_neighbor(tgen, topo1) - assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format( + assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error: {}".format( ospf_covergence ) @@ -1313,7 +1254,7 @@ def ospfv3_router_id_tc14_p2(request): reset_config_on_routers(tgen) ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format( + assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error: {}".format( ospf_covergence ) diff --git a/tests/topotests/pim_acl/r1/ospf_neighbor.json b/tests/topotests/pim_acl/r1/ospf_neighbor.json index af83d6bea3..34ce481a3f 100644 --- a/tests/topotests/pim_acl/r1/ospf_neighbor.json +++ b/tests/topotests/pim_acl/r1/ospf_neighbor.json @@ -2,57 +2,57 @@ "neighbors":{ "192.168.0.11":[ { - "priority":10, + "nbrPriority":10, "converged":"Full", - "address":"192.168.101.11", + "ifaceAddress":"192.168.101.11", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.12":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.12", + "ifaceAddress":"192.168.101.12", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.13":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.13", + "ifaceAddress":"192.168.101.13", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.14":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.14", + "ifaceAddress":"192.168.101.14", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.15":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.15", + "ifaceAddress":"192.168.101.15", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json index 1e70fcc36e..198098d3d3 100644 --- a/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json +++ b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json @@ -4,9 +4,9 @@ "neighbors":{ "192.168.0.11":[ { - "priority":10, + "nbrPriority":10, "converged":"Full", - "address":"192.168.101.11", + "ifaceAddress":"192.168.101.11", "ifaceName":"r1-eth1:192.168.101.1" } ] diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json index 7f2ab248cc..6fce225ffc 100644 --- a/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json +++ b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json @@ -4,9 +4,9 @@ "neighbors":{ "192.168.0.12":[ { - "priority":10, + "nbrPriority":10, "converged":"Full", - "address":"192.168.101.12", + "ifaceAddress":"192.168.101.12", "ifaceName":"r1-eth3:192.168.101.1" } ] diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index 6986e3051c..f779bf0a74 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -1,6 +1,8 @@ # Skip pytests example directory [pytest] +asyncio_mode = auto + # We always turn this on inside conftest.py, default shown # addopts = --junitxml=<rundir>/topotests.xml @@ -24,7 +26,7 @@ log_file_date_format = %Y-%m-%d %H:%M:%S junit_logging = all junit_log_passing_tests = true -norecursedirs = .git example_test example_topojson_test lib docker +norecursedirs = .git example_munet example_test example_topojson_test lib munet docker # Directory to store test results and run logs in, default shown # rundir = /tmp/topotests diff --git a/tests/topotests/rip_allow_ecmp/__init__.py b/tests/topotests/rip_allow_ecmp/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/__init__.py diff --git a/tests/topotests/rip_allow_ecmp/r1/frr.conf b/tests/topotests/rip_allow_ecmp/r1/frr.conf new file mode 100644 index 0000000000..d8eb9a31d3 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r1/frr.conf @@ -0,0 +1,9 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router rip + allow-ecmp + network 192.168.1.0/24 + timers basic 5 15 10 +exit diff --git a/tests/topotests/rip_allow_ecmp/r2/frr.conf b/tests/topotests/rip_allow_ecmp/r2/frr.conf new file mode 100644 index 0000000000..d7ea6f3102 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r2/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_allow_ecmp/r3/frr.conf b/tests/topotests/rip_allow_ecmp/r3/frr.conf new file mode 100644 index 0000000000..2362c47b84 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r3/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r3-eth0 + ip address 192.168.1.3/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py new file mode 100644 index 0000000000..acc0aea9e8 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if RIP `allow-ecmp` command works correctly. +""" + +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 + +pytestmark = [pytest.mark.ripd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2", "r3")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_rip_allow_ecmp(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _show_rip_routes(): + xpath = ( + "/frr-ripd:ripd/instance[vrf='default']" + "/state/routes/route[prefix='10.10.10.1/32']" + ) + try: + output = json.loads( + r1.vtysh_cmd(f"show yang operational-data {xpath} ripd") + ) + except Exception: + return False + + try: + output = output["frr-ripd:ripd"]["instance"][0]["state"]["routes"] + except KeyError: + return False + + expected = { + "route": [ + { + "prefix": "10.10.10.1/32", + "nexthops": { + "nexthop": [ + { + "nh-type": "ip4", + "protocol": "rip", + "rip-type": "normal", + "gateway": "192.168.1.2", + "from": "192.168.1.2", + "tag": 0, + }, + { + "nh-type": "ip4", + "protocol": "rip", + "rip-type": "normal", + "gateway": "192.168.1.3", + "from": "192.168.1.3", + "tag": 0, + }, + ] + }, + "metric": 2, + }, + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_show_rip_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip rip`" + + def _show_routes(): + output = json.loads(r1.vtysh_cmd("show ip route json")) + expected = { + "10.10.10.1/32": [ + { + "nexthops": [ + { + "ip": "192.168.1.2", + "active": True, + }, + { + "ip": "192.168.1.3", + "active": True, + }, + ] + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_show_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip route`" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/rip_bfd_topo1/__init__.py b/tests/topotests/rip_bfd_topo1/__init__.py new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/__init__.py diff --git a/tests/topotests/rip_bfd_topo1/r1/bfdd.conf b/tests/topotests/rip_bfd_topo1/r1/bfdd.conf new file mode 100644 index 0000000000..e848bda89f --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r1/bfdd.conf @@ -0,0 +1,6 @@ +bfd + profile slow + receive-interval 1000 + transmit-interval 1000 + exit +exit diff --git a/tests/topotests/rip_bfd_topo1/r1/ripd.conf b/tests/topotests/rip_bfd_topo1/r1/ripd.conf new file mode 100644 index 0000000000..6cef84692a --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r1/ripd.conf @@ -0,0 +1,17 @@ +interface r1-eth0 + ip rip bfd + ip rip bfd profile slow +exit +! +interface r1-eth1 + ip rip bfd + ip rip bfd profile slow +exit +! +router rip + allow-ecmp + network 192.168.0.1/24 + network 192.168.1.1/24 + redistribute connected + timers basic 10 40 30 +exit diff --git a/tests/topotests/rip_bfd_topo1/r1/zebra.conf b/tests/topotests/rip_bfd_topo1/r1/zebra.conf new file mode 100644 index 0000000000..e3800a9c68 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r1/zebra.conf @@ -0,0 +1,11 @@ +interface r1-eth0 + ip address 192.168.0.1/24 +exit +! +interface r1-eth1 + ip address 192.168.1.1/24 +exit +! +interface lo + ip address 10.254.254.1/32 +exit diff --git a/tests/topotests/rip_bfd_topo1/r2/bfdd.conf b/tests/topotests/rip_bfd_topo1/r2/bfdd.conf new file mode 100644 index 0000000000..e848bda89f --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r2/bfdd.conf @@ -0,0 +1,6 @@ +bfd + profile slow + receive-interval 1000 + transmit-interval 1000 + exit +exit diff --git a/tests/topotests/rip_bfd_topo1/r2/ripd.conf b/tests/topotests/rip_bfd_topo1/r2/ripd.conf new file mode 100644 index 0000000000..35e4688c58 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r2/ripd.conf @@ -0,0 +1,11 @@ +interface r2-eth0 + ip rip bfd +exit +! +router rip + bfd default-profile slow + network 192.168.0.2/24 + redistribute connected + redistribute static + timers basic 10 40 30 +exit diff --git a/tests/topotests/rip_bfd_topo1/r2/staticd.conf b/tests/topotests/rip_bfd_topo1/r2/staticd.conf new file mode 100644 index 0000000000..6fe93748a0 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r2/staticd.conf @@ -0,0 +1 @@ +ip route 10.254.254.100/32 lo diff --git a/tests/topotests/rip_bfd_topo1/r2/zebra.conf b/tests/topotests/rip_bfd_topo1/r2/zebra.conf new file mode 100644 index 0000000000..cad922f6bb --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r2/zebra.conf @@ -0,0 +1,8 @@ +interface r2-eth0 + ip address 192.168.0.2/24 +exit +! +interface lo + ip address 10.254.254.2/32 +exit + diff --git a/tests/topotests/rip_bfd_topo1/r3/bfdd.conf b/tests/topotests/rip_bfd_topo1/r3/bfdd.conf new file mode 100644 index 0000000000..e848bda89f --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r3/bfdd.conf @@ -0,0 +1,6 @@ +bfd + profile slow + receive-interval 1000 + transmit-interval 1000 + exit +exit diff --git a/tests/topotests/rip_bfd_topo1/r3/ripd.conf b/tests/topotests/rip_bfd_topo1/r3/ripd.conf new file mode 100644 index 0000000000..0df0bac531 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r3/ripd.conf @@ -0,0 +1,11 @@ +interface r3-eth0 + ip rip bfd + ip rip bfd profile slow +exit +! +router rip + network 192.168.1.2/24 + redistribute connected + redistribute static + timers basic 10 40 30 +exit diff --git a/tests/topotests/rip_bfd_topo1/r3/staticd.conf b/tests/topotests/rip_bfd_topo1/r3/staticd.conf new file mode 100644 index 0000000000..6fe93748a0 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r3/staticd.conf @@ -0,0 +1 @@ +ip route 10.254.254.100/32 lo diff --git a/tests/topotests/rip_bfd_topo1/r3/zebra.conf b/tests/topotests/rip_bfd_topo1/r3/zebra.conf new file mode 100644 index 0000000000..12ffeca42f --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r3/zebra.conf @@ -0,0 +1,7 @@ +interface r3-eth0 + ip address 192.168.1.2/24 +exit +! +interface lo + ip address 10.254.254.3/32 +exit diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot new file mode 100644 index 0000000000..1480a8f76d --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot @@ -0,0 +1,58 @@ +## 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 template { + label="rip_bfd_topo1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + s1 [ + shape=oval, + label="s1\n192.168.0.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="s1\n192.168.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- s1 [label="r1-eth0\n.1"]; + r2 -- s1 [label="r2-eth0\n.2"]; + + r1 -- s2 [label="r1-eth1\n.1"]; + r3 -- s2 [label="r1-eth0\n.2"]; +} diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png Binary files differnew file mode 100644 index 0000000000..e5e362e3e0 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py new file mode 100644 index 0000000000..71c90931fb --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_rip_bfd_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_rip_bfd_topo1.py: Test RIP BFD integration. +""" + +import sys +import re +import pytest + +from functools import partial +from lib import topotest +from lib.topogen import Topogen, TopoRouter +from lib.topolog import logger + +pytestmark = [ + pytest.mark.bfdd, + pytest.mark.ripd, +] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = { + "s1": ("r1", "r2"), + "s2": ("r1", "r3") + } + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for router_name, router in router_list.items(): + router.load_config(TopoRouter.RD_BFD, "bfdd.conf") + router.load_config(TopoRouter.RD_RIP, "ripd.conf") + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + if router_name in ["r2", "r3"]: + router.load_config(TopoRouter.RD_STATIC, "staticd.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +@pytest.fixture(autouse=True) +def skip_on_failure(tgen): + "Test if routers is still running otherwise skip tests" + if tgen.routers_have_failure(): + pytest.skip("skipped because of previous test failure") + + +def show_rip_json(router): + "Get router 'show ip rip' JSON output" + output = router.vtysh_cmd("show ip rip") + routes = output.splitlines()[6:] + json = {} + + for route in routes: + match = re.match( + r"(.)\((.)\)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)", route) + if match is None: + continue + + route_entry = { + "code": match[1], + "subCode": match[2], + "nextHop": match[4], + "metric": int(match[5]), + "from": match[6], + } + + if json.get(match[3]) is None: + json[match[3]] = [] + + json[match[3]].append(route_entry) + + return json + + +def expect_routes(router, routes, time_amount): + "Expect 'routes' in 'router'." + + def test_function(): + "Internal test function." + return topotest.json_cmp(show_rip_json(router), routes) + + _, result = topotest.run_and_expect(test_function, + None, + count=time_amount, + wait=1) + assert result is None, "Unexpected routing table in {}".format( + router.name) + + +def expect_bfd_peers(router, peers): + "Expect 'peers' in 'router' BFD status." + test_func = partial( + topotest.router_json_cmp, + router, + "show bfd peers json", + peers, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "{} BFD peer status mismatch".format(router) + + +def test_rip_convergence(tgen): + "Test that RIP learns the neighbor routes." + + expect_routes( + tgen.gears["r1"], { + "10.254.254.2/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.2" + }], + "10.254.254.3/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.1.2" + }], + "10.254.254.100/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.2", + }, { + "code": "R", + "subCode": "n", + "from": "192.168.1.2", + }] + }, 40) + + expect_bfd_peers(tgen.gears["r1"], [{ + "peer": "192.168.0.2", + "status": "up", + "receive-interval": 1000, + "transmit-interval": 1000, + }, { + "peer": "192.168.1.2", + "status": "up", + "receive-interval": 1000, + "transmit-interval": 1000, + }]) + + expect_routes( + tgen.gears["r2"], { + "10.254.254.1/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.1" + }], + "10.254.254.3/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.1" + }], + "10.254.254.100/32": [{ + "code": "S", + "subCode": "r", + "from": "self" + }] + }, 40) + + expect_bfd_peers(tgen.gears["r2"], [{ + "peer": "192.168.0.1", + "status": "up", + "receive-interval": 1000, + "transmit-interval": 1000, + }]) + + expect_routes( + tgen.gears["r3"], { + "10.254.254.1/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.1.1" + }], + "10.254.254.2/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.1.1" + }], + "10.254.254.100/32": [{ + "code": "S", + "subCode": "r", + "from": "self" + }] + }, 40) + + expect_bfd_peers(tgen.gears["r3"], [{ + "peer": "192.168.1.1", + "status": "up", + "receive-interval": 1000, + "transmit-interval": 1000, + }]) + + +def test_rip_bfd_convergence(tgen): + "Test that RIP drop the gone neighbor routes." + + tgen.gears["r3"].link_enable("r3-eth0", False) + + expect_routes( + tgen.gears["r1"], { + "10.254.254.2/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.2" + }], + "10.254.254.3/32": None, + "10.254.254.100/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.2", + }] + }, 6) + + expect_routes( + tgen.gears["r3"], { + "10.254.254.1/32": None, + "10.254.254.2/32": None, + "10.254.254.100/32": [{ + "code": "S", + "subCode": "r", + "from": "self" + }] + }, 6) + + +def test_memory_leak(tgen): + "Run the memory leak test and report results." + + 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/rip_passive_interface/__init__.py b/tests/topotests/rip_passive_interface/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/rip_passive_interface/__init__.py diff --git a/tests/topotests/rip_passive_interface/r1/frr.conf b/tests/topotests/rip_passive_interface/r1/frr.conf new file mode 100644 index 0000000000..d8eb9a31d3 --- /dev/null +++ b/tests/topotests/rip_passive_interface/r1/frr.conf @@ -0,0 +1,9 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router rip + allow-ecmp + network 192.168.1.0/24 + timers basic 5 15 10 +exit diff --git a/tests/topotests/rip_passive_interface/r2/frr.conf b/tests/topotests/rip_passive_interface/r2/frr.conf new file mode 100644 index 0000000000..d7ea6f3102 --- /dev/null +++ b/tests/topotests/rip_passive_interface/r2/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_passive_interface/r3/frr.conf b/tests/topotests/rip_passive_interface/r3/frr.conf new file mode 100644 index 0000000000..2362c47b84 --- /dev/null +++ b/tests/topotests/rip_passive_interface/r3/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r3-eth0 + ip address 192.168.1.3/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_passive_interface/test_rip_passive_interface.py b/tests/topotests/rip_passive_interface/test_rip_passive_interface.py new file mode 100644 index 0000000000..c2b28c4a3e --- /dev/null +++ b/tests/topotests/rip_passive_interface/test_rip_passive_interface.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if RIP `passive-interface default` and `no passive-interface IFNAME` +combination works as expected. +""" + +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.common_config import step + +pytestmark = [pytest.mark.ripd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2", "r3")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_rip_passive_interface(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _show_routes(nh_num): + output = json.loads(r1.vtysh_cmd("show ip route 10.10.10.1/32 json")) + expected = { + "10.10.10.1/32": [ + { + "internalNextHopNum": nh_num, + "internalNextHopActiveNum": nh_num, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_show_routes, 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 2" + + step("Configure `passive-interface default` on r2") + r2.vtysh_cmd( + """ + configure terminal + router rip + passive-interface default + """ + ) + + test_func = functools.partial(_show_routes, 1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 1" + + step("Configure `no passive-interface r2-eth0` on r2 towards r1") + r2.vtysh_cmd( + """ + configure terminal + router rip + no passive-interface r2-eth0 + """ + ) + + test_func = functools.partial(_show_routes, 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/rip_topo1/r1/rip_status.ref b/tests/topotests/rip_topo1/r1/rip_status.ref index 31ad46ab2e..19fff1a828 100644 --- a/tests/topotests/rip_topo1/r1/rip_status.ref +++ b/tests/topotests/rip_topo1/r1/rip_status.ref @@ -18,5 +18,5 @@ Routing Protocol is "rip" r1-eth3 Routing Information Sources: Gateway BadPackets BadRoutes Distance Last Update - 193.1.1.2 0 0 120 XX:XX:XX + 193.1.1.2 0 0 120 XX:XX:XX Distance: (default is 120) diff --git a/tests/topotests/rip_topo1/r2/rip_status.ref b/tests/topotests/rip_topo1/r2/rip_status.ref index 99841a62b0..468b7aece8 100644 --- a/tests/topotests/rip_topo1/r2/rip_status.ref +++ b/tests/topotests/rip_topo1/r2/rip_status.ref @@ -14,6 +14,6 @@ Routing Protocol is "rip" 193.1.2.0/24 Routing Information Sources: Gateway BadPackets BadRoutes Distance Last Update - 193.1.1.1 0 0 120 XX:XX:XX - 193.1.2.2 0 0 120 XX:XX:XX + 193.1.1.1 0 0 120 XX:XX:XX + 193.1.2.2 0 0 120 XX:XX:XX Distance: (default is 120) diff --git a/tests/topotests/rip_topo1/r3/rip_status.ref b/tests/topotests/rip_topo1/r3/rip_status.ref index 040d3c32a1..f423e83f24 100644 --- a/tests/topotests/rip_topo1/r3/rip_status.ref +++ b/tests/topotests/rip_topo1/r3/rip_status.ref @@ -12,5 +12,5 @@ Routing Protocol is "rip" 193.1.2.0/24 Routing Information Sources: Gateway BadPackets BadRoutes Distance Last Update - 193.1.2.1 0 0 120 XX:XX:XX + 193.1.2.1 0 0 120 XX:XX:XX Distance: (default is 120) diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index 6137471ea6..e2863218b0 100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -51,7 +51,8 @@ def config_macvlan(tgen, r_str, device, macvlan): def setup_module(mod): "Sets up the pytest environment" - topodef = {"s1": ("r1", "r1", "r1", "r1", "r1", "r1", "r1", "r1")} + # 8 links to 8 switches on r1 + topodef = {"s{}".format(x): ("r1",) for x in range(1, 9)} tgen = Topogen(topodef, mod.__name__) tgen.start_topology() |
