diff options
Diffstat (limited to 'tests/topotests')
470 files changed, 20238 insertions, 1585 deletions
diff --git a/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref b/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref index 044cffa..a4a4aba 100644 --- a/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref +++ b/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref @@ -8,6 +8,16 @@ C>* 192.168.6.0/26 is directly connected, r1-eth6, XX:XX:XX C>* 192.168.7.0/26 is directly connected, r1-eth7, XX:XX:XX C>* 192.168.8.0/26 is directly connected, r1-eth8, XX:XX:XX C>* 192.168.9.0/26 is directly connected, r1-eth9, XX:XX:XX +L>* 192.168.0.1/32 is directly connected, r1-eth0, XX:XX:XX +L>* 192.168.1.1/32 is directly connected, r1-eth1, XX:XX:XX +L>* 192.168.2.1/32 is directly connected, r1-eth2, XX:XX:XX +L>* 192.168.3.1/32 is directly connected, r1-eth3, XX:XX:XX +L>* 192.168.4.1/32 is directly connected, r1-eth4, XX:XX:XX +L>* 192.168.5.1/32 is directly connected, r1-eth5, XX:XX:XX +L>* 192.168.6.1/32 is directly connected, r1-eth6, XX:XX:XX +L>* 192.168.7.1/32 is directly connected, r1-eth7, XX:XX:XX +L>* 192.168.8.1/32 is directly connected, r1-eth8, XX:XX:XX +L>* 192.168.9.1/32 is directly connected, r1-eth9, XX:XX:XX O 192.168.0.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX O 192.168.3.0/26 [110/10] is directly connected, r1-eth3, weight 1, XX:XX:XX S>* 1.1.1.1/32 [1/0] is directly connected, r1-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref b/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref index ef12d61..a25b53a 100644 --- a/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref +++ b/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref @@ -19,6 +19,16 @@ C * fe80::/64 is directly connected, r1-eth6, XX:XX:XX C * fe80::/64 is directly connected, r1-eth7, XX:XX:XX C * fe80::/64 is directly connected, r1-eth8, XX:XX:XX C * fe80::/64 is directly connected, r1-eth9, XX:XX:XX +L>* fc00:0:0:1::1/128 is directly connected, r1-eth1, XX:XX:XX +L>* fc00:0:0:2::1/128 is directly connected, r1-eth2, XX:XX:XX +L>* fc00:0:0:3::1/128 is directly connected, r1-eth3, XX:XX:XX +L>* fc00:0:0:4::1/128 is directly connected, r1-eth4, XX:XX:XX +L>* fc00:0:0:5::1/128 is directly connected, r1-eth5, XX:XX:XX +L>* fc00:0:0:6::1/128 is directly connected, r1-eth6, XX:XX:XX +L>* fc00:0:0:7::1/128 is directly connected, r1-eth7, XX:XX:XX +L>* fc00:0:0:8::1/128 is directly connected, r1-eth8, XX:XX:XX +L>* fc00:0:0:9::1/128 is directly connected, r1-eth9, XX:XX:XX +L>* fc00::1/128 is directly connected, r1-eth0, XX:XX:XX O fc00:0:0:4::/64 [110/10] is directly connected, r1-eth4, weight 1, XX:XX:XX S>* 4:5::6:10/128 [1/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX S>* 4:5::6:11/128 [1/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX diff --git a/tests/topotests/all_protocol_startup/r1/ospf6d.conf b/tests/topotests/all_protocol_startup/r1/ospf6d.conf index 33c2650..31c904b 100644 --- a/tests/topotests/all_protocol_startup/r1/ospf6d.conf +++ b/tests/topotests/all_protocol_startup/r1/ospf6d.conf @@ -6,12 +6,12 @@ log file ospf6d.log ! debug ospf6 neighbor ! interface r1-eth4 - ipv6 ospf6 hello-interval 1 + ipv6 ospf6 area 0.0.0.0 + ipv6 ospf6 hello-interval 1 ! router ospf6 ospf6 router-id 192.168.0.1 log-adjacency-changes - interface r1-eth4 area 0.0.0.0 ! line vty ! diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref index 0246687..4464e23 100644 --- a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref +++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref @@ -1,4 +1,4 @@ -BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0 +BGP router identifier 192.168.0.1, local AS number 100 VRF default vrf-id 0 BGP table version 1 RIB entries 1, using XXXX bytes of memory Peers 2, using XXXX KiB of memory diff --git a/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref b/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref index deeae87..9baec12 100644 --- a/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref +++ b/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref @@ -1,4 +1,4 @@ -BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0 +BGP router identifier 192.168.0.1, local AS number 100 VRF default vrf-id 0 BGP table version 1 RIB entries 1, using XXXX bytes of memory Peers 4, using XXXX KiB of memory 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 c319477..e067cdb 100644 --- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -599,16 +599,16 @@ def test_nexthop_groups(): count = 0 dups = [] nhg_id = route_get_nhg_id("6.6.6.1/32") - while (len(dups) != 3) and count < 10: + while (len(dups) != 4) and count < 10: output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id) dups = re.findall(r"(via 1\.1\.1\.1)", output) - if len(dups) != 3: + if len(dups) != 4: count += 1 sleep(1) # Should find 3, itself is inactive - assert len(dups) == 3, ( + assert len(dups) == 4, ( "Route 6.6.6.1/32 with Nexthop Group ID=%d has wrong number of resolved nexthops" % nhg_id ) @@ -951,26 +951,24 @@ def test_bgp_summary(): actual = re.sub(r"Total number.*", "", actual) actual = re.sub(r"Displayed.*", "", actual) # Remove IPv4 Unicast Summary (Title only) - actual = re.sub(r"IPv4 Unicast Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv4 Unicast Summary:", "", actual) # Remove IPv4 Multicast Summary (all of it) - actual = re.sub(r"IPv4 Multicast Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv4 Multicast Summary:", "", actual) actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual) # Remove IPv4 VPN Summary (all of it) - actual = re.sub(r"IPv4 VPN Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv4 VPN Summary:", "", actual) actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual) # Remove IPv4 Encap Summary (all of it) - actual = re.sub(r"IPv4 Encap Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv4 Encap Summary:", "", actual) actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual) # Remove Unknown Summary (all of it) - actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) + actual = re.sub(r"Unknown Summary:", "", actual) actual = re.sub(r"No Unknown neighbor is configured", "", actual) # Make Connect/Active/Idle the same (change them all to Active) actual = re.sub(r" Connect ", " Active ", actual) actual = re.sub(r" Idle ", " Active ", actual) - actual = re.sub( - r"IPv4 labeled-unicast Summary \(VRF default\):", "", actual - ) + actual = re.sub(r"IPv4 labeled-unicast Summary:", "", actual) actual = re.sub( r"No IPv4 labeled-unicast neighbor is configured", "", actual ) @@ -1108,27 +1106,25 @@ def test_bgp_ipv6_summary(): actual = re.sub(r"Total number.*", "", actual) actual = re.sub(r"Displayed.*", "", actual) # Remove IPv4 Unicast Summary (Title only) - actual = re.sub(r"IPv6 Unicast Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv6 Unicast Summary:", "", actual) # Remove IPv4 Multicast Summary (all of it) - actual = re.sub(r"IPv6 Multicast Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv6 Multicast Summary:", "", actual) actual = re.sub(r"No IPv6 Multicast neighbor is configured", "", actual) # Remove IPv4 VPN Summary (all of it) - actual = re.sub(r"IPv6 VPN Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv6 VPN Summary:", "", actual) actual = re.sub(r"No IPv6 VPN neighbor is configured", "", actual) # Remove IPv4 Encap Summary (all of it) - actual = re.sub(r"IPv6 Encap Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv6 Encap Summary:", "", actual) actual = re.sub(r"No IPv6 Encap neighbor is configured", "", actual) # Remove Unknown Summary (all of it) - actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) + actual = re.sub(r"Unknown Summary:", "", actual) actual = re.sub(r"No Unknown neighbor is configured", "", actual) # Make Connect/Active/Idle the same (change them all to Active) actual = re.sub(r" Connect ", " Active ", actual) actual = re.sub(r" Idle ", " Active ", actual) # Remove Labeled Unicast Summary (all of it) - actual = re.sub( - r"IPv6 labeled-unicast Summary \(VRF default\):", "", actual - ) + actual = re.sub(r"IPv6 labeled-unicast Summary:", "", actual) actual = re.sub( r"No IPv6 labeled-unicast neighbor is configured", "", actual ) diff --git a/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf index 98da8c2..a8ce562 100644 --- a/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf +++ b/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf @@ -6,12 +6,14 @@ hostname rt1 password 1 ! interface eth-rt2 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ipv6 ospf6 bfd ! interface eth-rt3 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast @@ -19,7 +21,5 @@ interface eth-rt3 ! router ospf6 ospf6 router-id 1.1.1.1 - interface eth-rt2 area 0.0.0.0 - interface eth-rt3 area 0.0.0.0 redistribute connected ! diff --git a/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf index 34b0902..f04d017 100644 --- a/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf +++ b/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf @@ -5,19 +5,19 @@ hostname rt2 password 1 ! interface eth-rt1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ipv6 ospf6 bfd ! interface eth-rt5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ! router ospf6 ospf6 router-id 2.2.2.2 - interface eth-rt1 area 0.0.0.0 - interface eth-rt5 area 0.0.0.0 redistribute connected ! diff --git a/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf index 8ab4eee..faf9754 100644 --- a/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf +++ b/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf @@ -5,19 +5,19 @@ hostname rt3 password 1 ! interface eth-rt1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ipv6 ospf6 bfd ! interface eth-rt4 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ! router ospf6 ospf6 router-id 3.3.3.3 - interface eth-rt1 area 0.0.0.0 - interface eth-rt4 area 0.0.0.0 redistribute connected ! diff --git a/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf index 138b688..c96093b 100644 --- a/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf +++ b/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf @@ -5,18 +5,18 @@ hostname rt4 password 1 ! interface eth-rt3 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ! interface eth-rt5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ! router ospf6 ospf6 router-id 4.4.4.4 - interface eth-rt3 area 0.0.0.0 - interface eth-rt5 area 0.0.0.0 redistribute connected ! diff --git a/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf index 6eb4fe5..6d40d17 100644 --- a/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf +++ b/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf @@ -5,18 +5,18 @@ hostname rt5 password 1 ! interface eth-rt2 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ! interface eth-rt4 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ! router ospf6 ospf6 router-id 5.5.5.5 - interface eth-rt2 area 0.0.0.0 - interface eth-rt4 area 0.0.0.0 redistribute connected ! diff --git a/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf b/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf index 4ef28c3..948874c 100644 --- a/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf +++ b/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf @@ -1,4 +1,5 @@ interface r4-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 bfd profile fast-tx ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -6,5 +7,4 @@ interface r4-eth1 router ospf6 ospf6 router-id 10.254.254.4 redistribute connected - interface r4-eth1 area 0.0.0.0 ! diff --git a/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf b/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf index 20b53cf..f6e8dc3 100644 --- a/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf +++ b/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf @@ -1,4 +1,5 @@ interface r5-eth0 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 bfd ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -6,5 +7,4 @@ interface r5-eth0 router ospf6 ospf6 router-id 10.254.254.5 redistribute connected - interface r5-eth0 area 0.0.0.0 ! diff --git a/tests/topotests/bfd_topo3/test_bfd_topo3.py b/tests/topotests/bfd_topo3/test_bfd_topo3.py index c0dc052..f767b0e 100644 --- a/tests/topotests/bfd_topo3/test_bfd_topo3.py +++ b/tests/topotests/bfd_topo3/test_bfd_topo3.py @@ -65,6 +65,41 @@ def setup_module(mod): tgen.start_router() +def expect_static_bfd_output(router, filename): + "Load JSON file and compare with 'show bfd peer json'" + + tgen = get_topogen() + + logger.info("waiting BFD configuration on router {}".format(router)) + bfd_config = json.loads(open("{}/{}/{}.json".format(CWD, router, filename)).read()) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show bfd static route json", + bfd_config, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"{}" BFD static route status failure'.format(router) + assert result is None, assertmsg + + +def expect_route_missing(router, iptype, route): + "Wait until route is present on RIB for protocol." + + tgen = get_topogen() + + logger.info("waiting route {} to disapear in {}".format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show {} route json".format(iptype), + {route: None}, + ) + rv, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + def test_wait_bgp_convergence(): "Wait for BGP to converge" tgen = get_topogen() @@ -166,7 +201,7 @@ def test_wait_bfd_convergence(): expect_bfd_configuration("r6") -def test_static_route_monitoring(): +def test_static_route_monitoring_convergence(): "Test static route monitoring output." tgen = get_topogen() if tgen.routers_have_failure(): @@ -174,31 +209,47 @@ def test_static_route_monitoring(): logger.info("test BFD static route status") - def expect_static_bfd_output(router, filename): - "Load JSON file and compare with 'show bfd peer json'" - logger.info("waiting BFD configuration on router {}".format(router)) - bfd_config = json.loads( - open("{}/{}/{}.json".format(CWD, router, filename)).read() - ) - test_func = partial( - topotest.router_json_cmp, - tgen.gears[router], - "show bfd static route json", - bfd_config, - ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) - assertmsg = '"{}" BFD static route status failure'.format(router) - assert result is None, assertmsg - expect_static_bfd_output("r3", "bfd-static") expect_static_bfd_output("r6", "bfd-static") - logger.info("Setting r4 link down ...") - tgen.gears["r4"].link_enable("r4-eth0", False) +def test_static_route_monitoring_wrong_source(): + "Test that static monitoring fails if setting a wrong source." - expect_static_bfd_output("r3", "bfd-static-down") - expect_static_bfd_output("r6", "bfd-static-down") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test route wrong ") + + tgen.gears["r3"].vtysh_cmd( + """ +configure +ipv6 route 2001:db8:5::/64 2001:db8:4::3 bfd multi-hop source 2001:db8:4::2 profile slow-tx +""" + ) + + expect_route_missing("r3", "ipv6", "2001:db8:5::/64") + + +def test_static_route_monitoring_unset_source(): + "Test that static monitoring fails if setting a wrong source." + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test route wrong ") + + tgen.gears["r3"].vtysh_cmd( + """ +configure +ipv6 route 2001:db8:5::/64 2001:db8:4::3 bfd multi-hop profile slow-tx +""" + ) + + expect_static_bfd_output("r3", "bfd-static") + expect_static_bfd_output("r6", "bfd-static") def test_expect_static_rib_removal(): @@ -208,18 +259,12 @@ def test_expect_static_rib_removal(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - def expect_route_missing(router, iptype, route): - "Wait until route is present on RIB for protocol." - logger.info("waiting route {} to disapear in {}".format(route, router)) - test_func = partial( - topotest.router_json_cmp, - tgen.gears[router], - "show {} route json".format(iptype), - {route: None}, - ) - rv, result = topotest.run_and_expect(test_func, None, count=20, wait=1) - assertmsg = '"{}" convergence failure'.format(router) - assert result is None, assertmsg + logger.info("Setting r4 link down ...") + + tgen.gears["r4"].link_enable("r4-eth0", False) + + expect_static_bfd_output("r3", "bfd-static-down") + expect_static_bfd_output("r6", "bfd-static-down") expect_route_missing("r1", "ip", "10.254.254.5/32") expect_route_missing("r2", "ip", "10.254.254.5/32") diff --git a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py index a532f3a..9e5a68f 100644 --- a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py +++ b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py @@ -84,6 +84,7 @@ def setup_module(mod): router.net.set_intf_netns(rname + "-eth2", ns, up=True) for rname, router in router_list.items(): + router.load_config(TopoRouter.RD_MGMTD, None, "--vrfwnetns") router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), diff --git a/tests/topotests/bgp_accept_own/pe1/bgpd.conf b/tests/topotests/bgp_accept_own/pe1/bgpd.conf index 15466b4..1f7abac 100644 --- a/tests/topotests/bgp_accept_own/pe1/bgpd.conf +++ b/tests/topotests/bgp_accept_own/pe1/bgpd.conf @@ -25,7 +25,7 @@ router bgp 65001 vrf Customer neighbor 192.168.1.1 timers connect 1 address-family ipv4 unicast redistribute connected - label vpn export 10 + label vpn export 250 rd vpn export 192.168.1.2:2 rt vpn import 192.168.1.2:2 rt vpn export 192.168.1.2:2 @@ -40,7 +40,7 @@ router bgp 65001 vrf Service neighbor 192.168.2.1 timers 1 3 neighbor 192.168.2.1 timers connect 1 address-family ipv4 unicast - label vpn export 20 + label vpn export 350 rd vpn export 192.168.2.2:2 rt vpn import 192.168.2.2:2 rt vpn export 192.168.2.2:2 diff --git a/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py b/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py index cec0692..eeac714 100644 --- a/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py +++ b/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py @@ -74,7 +74,8 @@ def test_bgp_maximum_prefix_invalid(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r2"] + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] def _bgp_converge(router): output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) @@ -86,22 +87,30 @@ def test_bgp_maximum_prefix_invalid(): } return topotest.json_cmp(output, expected) - def _bgp_aggregate_address_has_metric(router): + def _bgp_aggregate_address_has_metric(router, metric): output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.0/24 json")) - expected = {"paths": [{"metric": 123}]} + expected = {"paths": [{"metric": metric}]} return topotest.json_cmp(output, expected) - test_func = functools.partial(_bgp_converge, router) - success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) - - assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) - - test_func = functools.partial(_bgp_aggregate_address_has_metric, router) - success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) - - assert ( - result is None - ), 'Failed to see applied metric for aggregated prefix in "{}"'.format(router) + test_func = functools.partial(_bgp_converge, r2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see bgp convergence in r2" + + test_func = functools.partial(_bgp_aggregate_address_has_metric, r2, 123) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see applied metric for aggregated prefix in r2" + + r1.vtysh_cmd( + """ + configure terminal + route-map aggr-rmap permit 10 + set metric 666 + """ + ) + + test_func = functools.partial(_bgp_aggregate_address_has_metric, r2, 666) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see applied metric for aggregated prefix in r2" if __name__ == "__main__": diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp.py b/tests/topotests/bgp_bmp/test_bgp_bmp.py index 65f191b..250f1cb 100644 --- a/tests/topotests/bgp_bmp/test_bgp_bmp.py +++ b/tests/topotests/bgp_bmp/test_bgp_bmp.py @@ -49,6 +49,7 @@ SEQ = 0 PRE_POLICY = "pre-policy" POST_POLICY = "post-policy" +LOC_RIB = "loc-rib" def build_topo(tgen): @@ -120,7 +121,7 @@ def get_bmp_messages(): return messages -def check_for_prefixes(expected_prefixes, bmp_log_type, post_policy): +def check_for_prefixes(expected_prefixes, bmp_log_type, policy): """ Check for the presence of the given prefixes in the BMP server logs with the given message type and the set policy. @@ -138,7 +139,7 @@ def check_for_prefixes(expected_prefixes, bmp_log_type, post_policy): if "ip_prefix" in m.keys() and "bmp_log_type" in m.keys() and m["bmp_log_type"] == bmp_log_type - and m["post_policy"] == post_policy + and m["policy"] == policy ] # check for prefixes @@ -202,7 +203,7 @@ def unicast_prefixes(policy): logger.info("checking for updated prefixes") # check - test_func = partial(check_for_prefixes, prefixes, "update", policy == POST_POLICY) + test_func = partial(check_for_prefixes, prefixes, "update", policy) success, _ = topotest.run_and_expect(test_func, True, wait=0.5) assert success, "Checking the updated prefixes has been failed !." @@ -210,7 +211,7 @@ def unicast_prefixes(policy): configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, update=False) logger.info("checking for withdrawed prefxies") # check - test_func = partial(check_for_prefixes, prefixes, "withdraw", policy == POST_POLICY) + test_func = partial(check_for_prefixes, prefixes, "withdraw", policy) success, _ = topotest.run_and_expect(test_func, True, wait=0.5) assert success, "Checking the withdrawed prefixes has been failed !." @@ -239,6 +240,8 @@ def test_bmp_bgp_unicast(): unicast_prefixes(PRE_POLICY) logger.info("*** Unicast prefixes post-policy logging ***") unicast_prefixes(POST_POLICY) + logger.info("*** Unicast prefixes loc-rib logging ***") + unicast_prefixes(LOC_RIB) if __name__ == "__main__": diff --git a/tests/topotests/bgp_default_route/r1/bgpd.conf b/tests/topotests/bgp_default_route/r1/bgpd.conf index 8699d62..10ced36 100644 --- a/tests/topotests/bgp_default_route/r1/bgpd.conf +++ b/tests/topotests/bgp_default_route/r1/bgpd.conf @@ -3,6 +3,7 @@ router bgp 65000 neighbor 192.168.255.2 remote-as 65001 neighbor 192.168.255.2 timers 3 10 address-family ipv4 unicast + network 0.0.0.0/1 neighbor 192.168.255.2 default-originate exit-address-family ! diff --git a/tests/topotests/bgp_default_route/r1/zebra.conf b/tests/topotests/bgp_default_route/r1/zebra.conf index 0a283c0..fbf97b0 100644 --- a/tests/topotests/bgp_default_route/r1/zebra.conf +++ b/tests/topotests/bgp_default_route/r1/zebra.conf @@ -1,4 +1,6 @@ ! +ip route 0.0.0.0/1 blackhole +! interface lo ip address 172.16.255.254/32 ! diff --git a/tests/topotests/bgp_default_route/test_bgp_default-originate.py b/tests/topotests/bgp_default_route/test_bgp_default-originate.py index 2463b05..333beb0 100644 --- a/tests/topotests/bgp_default_route/test_bgp_default-originate.py +++ b/tests/topotests/bgp_default_route/test_bgp_default-originate.py @@ -69,18 +69,18 @@ def test_bgp_default_originate_route_map(): expected = { "192.168.255.1": { "bgpState": "Established", - "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, } } return topotest.json_cmp(output, expected) def _bgp_check_if_originated(): output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp summary json")) - expected = {"ipv4Unicast": {"peers": {"192.168.255.2": {"pfxSnt": 1}}}} + expected = {"ipv4Unicast": {"peers": {"192.168.255.2": {"pfxSnt": 2}}}} return topotest.json_cmp(output, expected) - def _bgp_default_route_is_valid(router): - output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + def _bgp_route_is_valid(router, prefix): + output = json.loads(router.vtysh_cmd("show ip bgp {} json".format(prefix))) expected = {"paths": [{"valid": True}]} return topotest.json_cmp(output, expected) @@ -92,10 +92,14 @@ def test_bgp_default_originate_route_map(): success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) assert result is None, "No 0.0.0.0/0 from r1 to r2" - test_func = functools.partial(_bgp_default_route_is_valid, tgen.gears["r2"]) + test_func = functools.partial(_bgp_route_is_valid, tgen.gears["r2"], "0.0.0.0/0") success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) assert result is None, "Failed to see 0.0.0.0/0 in r2" + test_func = functools.partial(_bgp_route_is_valid, tgen.gears["r2"], "0.0.0.0/1") + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see 0.0.0.0/1 in r2" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf index 6f6d394..c442e06 100644 --- a/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf +++ b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf @@ -1,12 +1,17 @@ -router bgp 65000 - no bgp ebgp-requires-policy - neighbor 192.168.255.2 remote-as 65001 - neighbor 192.168.255.2 timers 3 10 - address-family ipv4 unicast - neighbor 192.168.255.2 default-originate route-map default - exit-address-family +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 PG peer-group + neighbor PG remote-as external + neighbor PG timers 3 10 + bgp listen range 192.168.255.0/24 peer-group PG + address-family ipv4 unicast + neighbor PG default-originate route-map default + neighbor 192.168.255.2 default-originate route-map default + exit-address-family ! route-map default permit 10 - set metric 123 - set as-path prepend 65000 65000 65000 + set metric 123 + set as-path prepend 65001 65001 65001 ! diff --git a/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf index 0a283c0..4af88e5 100644 --- a/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf +++ b/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf @@ -5,5 +5,3 @@ interface lo interface r1-eth0 ip address 192.168.255.1/24 ! -ip forwarding -! diff --git a/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf index 00c96cc..3a18a11 100644 --- a/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf +++ b/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf @@ -1,8 +1,8 @@ -router bgp 65001 - no bgp ebgp-requires-policy - neighbor 192.168.255.1 remote-as 65000 - neighbor 192.168.255.1 timers 3 10 - address-family ipv4 unicast - redistribute connected - exit-address-family +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as external + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family ! diff --git a/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf index 606c17b..c03dd7e 100644 --- a/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf +++ b/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf @@ -2,5 +2,3 @@ interface r2-eth0 ip address 192.168.255.2/24 ! -ip forwarding -! diff --git a/tests/topotests/bgp_default_route_route_map_set/r3/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r3/bgpd.conf new file mode 100644 index 0000000..c477037 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_set/r3/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as external + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_default_route_route_map_set/r3/zebra.conf b/tests/topotests/bgp_default_route_route_map_set/r3/zebra.conf new file mode 100644 index 0000000..5ae9daf --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_set/r3/zebra.conf @@ -0,0 +1,4 @@ +! +interface r3-eth0 + ip address 192.168.255.3/24 +! diff --git a/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py index 3bd900b..e633b61 100644 --- a/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py +++ b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py @@ -26,12 +26,13 @@ pytestmark = [pytest.mark.bgpd] def build_topo(tgen): - for routern in range(1, 3): + for routern in range(1, 4): tgen.add_router("r{}".format(routern)) switch = tgen.add_switch("s1") switch.add_link(tgen.gears["r1"]) switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) def setup_module(mod): @@ -62,14 +63,17 @@ def test_bgp_default_originate_route_map(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r2"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] - def _bgp_converge(router): + def _bgp_converge(router, pfxCount): output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) expected = { "192.168.255.1": { "bgpState": "Established", - "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + "addressFamilyInfo": { + "ipv4Unicast": {"acceptedPrefixCounter": pfxCount} + }, } } return topotest.json_cmp(output, expected) @@ -77,21 +81,25 @@ def test_bgp_default_originate_route_map(): def _bgp_default_route_has_metric(router): output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) expected = { - "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}] + "paths": [{"aspath": {"string": "65001 65001 65001 65001"}, "metric": 123}] } return topotest.json_cmp(output, expected) - test_func = functools.partial(_bgp_converge, router) - success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + test_func = functools.partial(_bgp_converge, r2, 1) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to see bgp convergence in r2" - assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + test_func = functools.partial(_bgp_default_route_has_metric, r2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to see applied metric for default route in r2" - test_func = functools.partial(_bgp_default_route_has_metric, router) - success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + test_func = functools.partial(_bgp_converge, r3, 2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to see bgp convergence in r3" - assert ( - result is None - ), 'Failed to see applied metric for default route in "{}"'.format(router) + test_func = functools.partial(_bgp_default_route_has_metric, r3) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to see applied metric for default route in r3" if __name__ == "__main__": diff --git a/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py b/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py index ab71f87..9a0f562 100644 --- a/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py +++ b/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py @@ -122,8 +122,9 @@ def test_bgp_check_fqdn(): step("Wait to converge") test_func = functools.partial(bgp_converge, r1) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) - assert result is None, "Can't converge with dont-capability-negotiate" + assert result is None, "Can't converge with all capabilities" + step("Make sure FQDN capability is set") test_func = functools.partial(_bgp_check_fqdn, "r2") _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert result is None, "FQDN capability enabled, but r1 can't see it" @@ -150,6 +151,48 @@ def test_bgp_check_fqdn(): _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert result is None, "FQDN capability disabled, but we still have a hostname" + step("Re-enable sending any capability from r2") + r2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + address-family ipv4 unicast + no neighbor 192.168.1.1 dont-capability-negotiate + end + clear bgp 192.168.1.1 + """ + ) + step("Wait to converge") + tgen = get_topogen() + test_func = functools.partial(bgp_converge, r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge with all capabilities re enabled" + + step("Make sure FQDN capability is r2") + test_func = functools.partial(_bgp_check_fqdn, "r2") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "FQDN capability enabled, but r1 can't see it" + + step("Disable sending fqdn capability") + r2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + no neighbor 192.168.1.1 capability fqdn + end + clear bgp 192.168.1.1 + """ + ) + step("Wait to converge") + tgen = get_topogen() + test_func = functools.partial(bgp_converge, r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge with no capability fqdn" + + step("Make sure FQDN capability is reset") + test_func = functools.partial(_bgp_check_fqdn) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "FQDN capability disabled, but we still have a hostname" if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_dynamic_capability/r1/frr.conf b/tests/topotests/bgp_dynamic_capability/r1/frr.conf index 50280a9..aa5c3db 100644 --- a/tests/topotests/bgp_dynamic_capability/r1/frr.conf +++ b/tests/topotests/bgp_dynamic_capability/r1/frr.conf @@ -12,4 +12,10 @@ router bgp 65001 neighbor 192.168.1.2 timers 1 3 neighbor 192.168.1.2 timers connect 1 neighbor 192.168.1.2 capability dynamic + ! + address-family ipv4 unicast + neighbor 192.168.1.2 addpath-tx-all-paths + exit-address-family +! +ip prefix-list r2 seq 5 permit 10.10.10.10/32 ! diff --git a/tests/topotests/bgp_dynamic_capability/r2/frr.conf b/tests/topotests/bgp_dynamic_capability/r2/frr.conf index 16b83a5..7f25665 100644 --- a/tests/topotests/bgp_dynamic_capability/r2/frr.conf +++ b/tests/topotests/bgp_dynamic_capability/r2/frr.conf @@ -3,6 +3,7 @@ ! int lo ip address 10.10.10.10/32 + ip address 10.10.10.20/32 ! int r2-eth0 ip address 192.168.1.2/24 diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py new file mode 100644 index 0000000..5202f51 --- /dev/null +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if Addpath capability is adjusted dynamically. +""" + +import os +import re +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 +from lib.common_config import step + +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 _, (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_dynamic_capability_addpath(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "addPath": { + "ipv4Unicast": { + "txAdvertised": True, + "rxAdvertisedAndReceived": True, + } + }, + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 3, + } + }, + } + } + 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, "Can't converge" + + step("Enable Addpath capability and check if it's exchanged dynamically") + + # Clear message stats to check if we receive a notification or not after we + # change the settings fo LLGR. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r2.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + neighbor 192.168.1.1 addpath-tx-all-paths + """ + ) + + def _bgp_check_if_addpath_rx_tx_and_session_not_reset(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "addPath": { + "ipv4Unicast": { + "txAdvertisedAndReceived": True, + "rxAdvertisedAndReceived": True, + } + }, + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 3, + } + }, + "messageStats": { + "notificationsRecv": 0, + "capabilityRecv": 1, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_addpath_rx_tx_and_session_not_reset, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Session was reset after enabling Addpath capability" + + step("Disable Addpath capability RX and check if it's exchanged dynamically") + + # Clear message stats to check if we receive a notification or not after we + # disable addpath-rx. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r2.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + neighbor 192.168.1.1 disable-addpath-rx + """ + ) + + def _bgp_check_if_addpath_tx_and_session_not_reset(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "addPath": { + "ipv4Unicast": { + "txAdvertisedAndReceived": True, + "rxAdvertised": True, + } + }, + }, + "messageStats": { + "notificationsRecv": 0, + "capabilityRecv": 1, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_addpath_tx_and_session_not_reset, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Session was reset after disabling Addpath RX flags" + + # Clear message stats to check if we receive a notification or not after we + # disable Addpath capability. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + no neighbor 192.168.1.2 addpath-tx-all-paths + """ + ) + + def _bgp_check_if_addpath_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "addPath": { + "ipv4Unicast": { + "txAdvertisedAndReceived": None, + "txAdvertised": None, + "rxAdvertised": True, + } + }, + }, + "messageStats": { + "notificationsRecv": 0, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_addpath_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable Addpath capability" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_fqdn.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_fqdn.py new file mode 100644 index 0000000..338886d --- /dev/null +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_fqdn.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2024 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if fqdn capability is exchanged dynamically. +""" + +import os +import re +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 +from lib.common_config import step + +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 _, (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_dynamic_capability_fqdn(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "hostName": { + "advHostName": "r1", + "rcvHostName": "r2", + }, + }, + } + } + 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, "Can't converge" + + step("Disable fqdn capability and check if it's exchanged dynamically") + + # Clear message stats to check if we receive a notification or not after we + # disable fqdn capability. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + no neighbor 192.168.1.2 capability fqdn + """ + ) + + def _bgp_check_if_fqdn_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "hostName": { + "advHostName": None, + "rcvHostName": "r2", + }, + }, + "messageStats": { + "notificationsRecv": 0, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_fqdn_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable fqdn capability" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py index 75c712e..4644ef3 100644 --- a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py @@ -70,7 +70,7 @@ def test_bgp_dynamic_capability_graceful_restart(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, "gracefulRestartInfo": { @@ -123,7 +123,7 @@ def test_bgp_dynamic_capability_graceful_restart(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, "gracefulRestartInfo": { @@ -159,7 +159,7 @@ def test_bgp_dynamic_capability_graceful_restart(): ) # Clear message stats to check if we receive a notification or not after we - # change the settings fo LLGR. + # disable graceful-restart notification support. r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") r2.vtysh_cmd( """ @@ -207,6 +207,40 @@ def test_bgp_dynamic_capability_graceful_restart(): result is None ), "Session was reset after changing Graceful-Restart notification support" + # Clear message stats to check if we receive a notification or not after we + # disable GR. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + bgp graceful-restart-disable + """ + ) + + def _bgp_check_if_gr_llgr_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "gracefulRestartCapability": "received", + "longLivedGracefulRestart": "received", + }, + "messageStats": { + "notificationsRecv": 0, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_gr_llgr_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable GR/LLGR capabilities" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_orf.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_orf.py new file mode 100644 index 0000000..ba95bd1 --- /dev/null +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_orf.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if ORF capability is adjusted dynamically. +""" + +import os +import re +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 +from lib.common_config import step + +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 _, (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_dynamic_capability_orf(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 3, + } + }, + } + } + 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, "Can't converge" + + step( + "Apply incoming prefix-list to r1 and check if we advertise only 10.10.10.20/32 from r2" + ) + + # Clear message stats to check if we receive a notification or not after we + # enable ORF capability. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + neighbor 192.168.1.2 prefix-list r2 in + neighbor 192.168.1.2 capability orf prefix-list both + """ + ) + + r2.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + neighbor 192.168.1.1 capability orf prefix-list both + """ + ) + + def _bgp_check_if_session_not_reset(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + }, + "messageStats": { + "notificationsRecv": 0, + "notificationsSent": 0, + "capabilityRecv": 1, + "capabilitySent": 1, + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 1, + "afDependentCap": { + "orfPrefixList": { + "sendMode": "advertisedAndReceived", + "recvMode": "advertisedAndReceived", + } + }, + "incomingUpdatePrefixFilterList": "r2", + } + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_session_not_reset, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Session was reset after setting up ORF capability" + + r1.vtysh_cmd( + """ + configure terminal + ip prefix-list r2 seq 5 permit 10.10.10.20/32 + """ + ) + + def _bgp_check_if_we_send_correct_prefix(): + output = json.loads( + r2.vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.1 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.20/32": { + "valid": True, + }, + }, + "totalPrefixCounter": 1, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_we_send_correct_prefix, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "Only 10.10.10.20/32 SHOULD be advertised due to ORF filtering" + + # Clear message stats to check if we receive a notification or not after we + # disable ORF capability. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + no neighbor 192.168.1.2 capability orf prefix-list both + """ + ) + + def _bgp_check_if_orf_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + }, + "messageStats": { + "notificationsRecv": 0, + "notificationsSent": 0, + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 1, + "afDependentCap": { + "orfPrefixList": { + "sendMode": "received", + "recvMode": "received", + } + }, + } + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_orf_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable ORF capability" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py index 576ef74..aa9ad5f 100644 --- a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py @@ -68,7 +68,7 @@ def test_bgp_dynamic_capability_role(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, } @@ -84,7 +84,7 @@ def test_bgp_dynamic_capability_role(): step("Set local-role and check if it's exchanged dynamically") # Clear message stats to check if we receive a notification or not after we - # change the settings fo LLGR. + # change the role. r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") r1.vtysh_cmd( """ @@ -115,7 +115,7 @@ def test_bgp_dynamic_capability_role(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, "messageStats": { @@ -132,6 +132,41 @@ def test_bgp_dynamic_capability_role(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assert result is None, "Session was reset after setting role capability" + # Clear message stats to check if we receive a notification or not after we + # change the role. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + no neighbor 192.168.1.2 local-role customer + """ + ) + + def _bgp_check_if_role_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "localRole": "undefined", + "remoteRole": "provider", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "role": "received", + }, + "messageStats": { + "notificationsRecv": 0, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_role_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable role capability" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py index 002d782..737e694 100644 --- a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py @@ -70,7 +70,7 @@ def test_bgp_dynamic_capability_software_version(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, } @@ -86,7 +86,7 @@ def test_bgp_dynamic_capability_software_version(): step("Enable software version capability and check if it's exchanged dynamically") # Clear message stats to check if we receive a notification or not after we - # change the settings fo LLGR. + # enable software-version capability. r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") r1.vtysh_cmd( """ @@ -136,7 +136,7 @@ def test_bgp_dynamic_capability_software_version(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, "messageStats": { @@ -155,6 +155,41 @@ def test_bgp_dynamic_capability_software_version(): result is None ), "Session was reset after enabling software version capability" + # Clear message stats to check if we receive a notification or not after we + # disable software-version capability. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + no neighbor 192.168.1.2 capability software-version + """ + ) + + def _bgp_check_if_software_version_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "softwareVersion": { + "advertisedSoftwareVersion": None, + }, + }, + "messageStats": { + "notificationsRecv": 0, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_software_version_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable software version capability" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_ecmp_topo1/exabgp.env b/tests/topotests/bgp_ecmp_topo1/exabgp.env index a328e04..ec978c6 100644 --- a/tests/topotests/bgp_ecmp_topo1/exabgp.env +++ b/tests/topotests/bgp_ecmp_topo1/exabgp.env @@ -1,5 +1,6 @@ [exabgp.api] +ack = false encoder = text highres = false respawn = false diff --git a/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg index 2d0ca89..97a024c 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 1 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 1"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 1 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.101; - local-address 10.0.1.101; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 1; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.101; + local-address 10.0.1.101; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg index 0c842a0..e318162 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 10 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 10"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 10 10; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.110; - local-address 10.0.2.110; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 10; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.110; + local-address 10.0.2.110; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg index 936dc57..ea5bdcc 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 11 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 11"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 11 10; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.111; - local-address 10.0.3.111; - local-as 111; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 11; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.111; + local-address 10.0.3.111; + local-as 111; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg index 56b33ea..81fb503 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 12 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 12"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 12 10; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.112; - local-address 10.0.3.112; - local-as 112; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 12; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.112; + local-address 10.0.3.112; + local-as 112; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg index b933ffb..4007841 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 13 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 13"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 13 10; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.113; - local-address 10.0.3.113; - local-as 113; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 13; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.113; + local-address 10.0.3.113; + local-as 113; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg index bcfa41e..afb8741 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 14 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 14"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 14 10; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.114; - local-address 10.0.3.114; - local-as 114; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 14; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.114; + local-address 10.0.3.114; + local-as 114; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg index 022e835..9a4ca7f 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 15 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 15"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 15 10; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.115; - local-address 10.0.3.115; - local-as 115; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 15; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.115; + local-address 10.0.3.115; + local-as 115; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg index 0649202..a0bb88a 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 16 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 16"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 16 10; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.116; - local-address 10.0.4.116; - local-as 116; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 16; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.116; + local-address 10.0.4.116; + local-as 116; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg index 0aeeed9..870d33d 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 17 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 17"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 17 10; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.117; - local-address 10.0.4.117; - local-as 117; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 17; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.117; + local-address 10.0.4.117; + local-as 117; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg index 352c030..c5a1ca6 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 18 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 18"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 18 10; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.118; - local-address 10.0.4.118; - local-as 118; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 18; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.118; + local-address 10.0.4.118; + local-as 118; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg index 9913c22..f662ccf 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 19 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 19"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 19 10; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.119; - local-address 10.0.4.119; - local-as 119; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 19; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.119; + local-address 10.0.4.119; + local-as 119; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg index 46b436d..673d6ec 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 2 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 2"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 2 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.102; - local-address 10.0.1.102; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 2; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.102; + local-address 10.0.1.102; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg index 17fb816..5ea2c91 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 20 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 20"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 20 10; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.120; - local-address 10.0.4.120; - local-as 120; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 20; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.120; + local-address 10.0.4.120; + local-as 120; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg index acd5775..47a25ca 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 3 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 3"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 3 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.103; - local-address 10.0.1.103; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 3; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.103; + local-address 10.0.1.103; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg index 4c9a989..376ac5f 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 4 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 4"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 4 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.104; - local-address 10.0.1.104; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 4; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.104; + local-address 10.0.1.104; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg index eba2aae..878d728 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 5 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 5"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 5 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.105; - local-address 10.0.1.105; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 5; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.105; + local-address 10.0.1.105; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg index 38b6af0..fc70267 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 6 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 6"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 6 10; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.106; - local-address 10.0.2.106; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 6; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.106; + local-address 10.0.2.106; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg index 7631e43..09827a8 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 7 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 7"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 7 10; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.107; - local-address 10.0.2.107; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 7; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.107; + local-address 10.0.2.107; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg index 1cd1cd9..37c01da 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 8 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 8"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 8 10; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.108; - local-address 10.0.2.108; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 8; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.108; + local-address 10.0.2.108; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg index 5771553..8fb5c58 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 9 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 9"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 9 10; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.109; - local-address 10.0.2.109; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 9; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.109; + local-address 10.0.2.109; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py b/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py index 97751ec..7af3f48 100644 --- a/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py +++ b/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # SPDX-License-Identifier: ISC # diff --git a/tests/topotests/bgp_evpn_mh/test_evpn_mh.py b/tests/topotests/bgp_evpn_mh/test_evpn_mh.py index ec52278..945c038 100644 --- a/tests/topotests/bgp_evpn_mh/test_evpn_mh.py +++ b/tests/topotests/bgp_evpn_mh/test_evpn_mh.py @@ -264,7 +264,7 @@ def config_bridge(node): node.run("ip link set dev bridge type bridge mcast_snooping 0") node.run("ip link set dev bridge type bridge vlan_stats_enabled 1") node.run("ip link set dev bridge up") - node.run("/sbin/bridge vlan add vid 1000 dev bridge") + node.run("/sbin/bridge vlan add vid 1000 dev bridge self") def config_vxlan(node, node_ip): diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py b/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py index 1066269..2041a40 100755 --- a/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py @@ -58,7 +58,6 @@ from lib.common_config import ( step, write_test_header, write_test_footer, - generate_support_bundle, ) # Required to instantiate the topology builder class. @@ -277,8 +276,6 @@ def test_evpn_gateway_ip_basic_topo(request): result, assertmsg = evpn_gateway_ip_show_op_check("base") - if result is not None: - generate_support_bundle() assert result is None, assertmsg write_test_footer(tc_name) @@ -319,8 +316,6 @@ def test_evpn_gateway_ip_flap_rt5(request): ) result, assertmsg = evpn_gateway_ip_show_op_check("no_rt5") - if result is not None: - generate_support_bundle() assert result is None, assertmsg step("Advertise type-5 routes again") @@ -339,8 +334,6 @@ def test_evpn_gateway_ip_flap_rt5(request): ) result, assertmsg = evpn_gateway_ip_show_op_check("base") - if result is not None: - generate_support_bundle() assert result is None, assertmsg @@ -371,8 +364,6 @@ def test_evpn_gateway_ip_flap_rt2(request): pe1.cmd_raises("ip link set dev vxlan100 down") result, assertmsg = evpn_gateway_ip_show_op_check("no_rt2") - if result is not None: - generate_support_bundle() assert result is None, assertmsg step("Bring up VxLAN interface at PE1 and advertise type-2 routes again") @@ -380,8 +371,6 @@ def test_evpn_gateway_ip_flap_rt2(request): pe1.cmd_raises("ip link set dev vxlan100 up") result, assertmsg = evpn_gateway_ip_show_op_check("base") - if result is not None: - generate_support_bundle() assert result is None, assertmsg write_test_footer(tc_name) diff --git a/tests/topotests/bgp_evpn_route_map_match/__init__.py b/tests/topotests/bgp_evpn_route_map_match/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/topotests/bgp_evpn_route_map_match/__init__.py diff --git a/tests/topotests/bgp_evpn_route_map_match/c1/frr.conf b/tests/topotests/bgp_evpn_route_map_match/c1/frr.conf new file mode 100644 index 0000000..7476a37 --- /dev/null +++ b/tests/topotests/bgp_evpn_route_map_match/c1/frr.conf @@ -0,0 +1,4 @@ +! +int c1-eth0 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bgp_evpn_route_map_match/c2/frr.conf b/tests/topotests/bgp_evpn_route_map_match/c2/frr.conf new file mode 100644 index 0000000..a203daa --- /dev/null +++ b/tests/topotests/bgp_evpn_route_map_match/c2/frr.conf @@ -0,0 +1,4 @@ +! +int c2-eth0 + ip address 192.168.0.2/24 +! diff --git a/tests/topotests/bgp_evpn_route_map_match/r1/frr.conf b/tests/topotests/bgp_evpn_route_map_match/r1/frr.conf new file mode 100644 index 0000000..4347052 --- /dev/null +++ b/tests/topotests/bgp_evpn_route_map_match/r1/frr.conf @@ -0,0 +1,44 @@ +! +!debug bgp neighbor +!debug route-map detail +! +vni 10 +! +int lo + ip address 10.10.10.1/32 +! +int r1-eth1 + ip address 192.168.1.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + ! + address-family ipv4 unicast + redistribute connected + network 10.10.10.10/32 + exit-address-family + ! + address-family l2vpn evpn + neighbor 192.168.1.2 activate + neighbor 192.168.1.2 route-map r2 out + advertise-all-vni + advertise ipv4 unicast + exit-address-family +! +route-map r2 deny 10 + match evpn route-type macip +! +route-map r2 deny 20 + match ip address prefix-list pl + match evpn route-type prefix +! +route-map r2 permit 30 +! +ip prefix-list pl seq 5 permit 192.168.1.0/24 +ip prefix-list pl seq 10 permit 10.10.10.1/32 +ip prefix-list pl seq 15 permit 10.10.10.2/32 +! diff --git a/tests/topotests/bgp_evpn_route_map_match/r2/frr.conf b/tests/topotests/bgp_evpn_route_map_match/r2/frr.conf new file mode 100644 index 0000000..9ed298d --- /dev/null +++ b/tests/topotests/bgp_evpn_route_map_match/r2/frr.conf @@ -0,0 +1,24 @@ +! +!debug bgp neighbor +! +int lo + ip address 10.10.10.2/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family l2vpn evpn + neighbor 192.168.1.1 activate + advertise-all-vni + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_route_map_match/test_bgp_evpn_route_map_match.py b/tests/topotests/bgp_evpn_route_map_match/test_bgp_evpn_route_map_match.py new file mode 100644 index 0000000..5781684 --- /dev/null +++ b/tests/topotests/bgp_evpn_route_map_match/test_bgp_evpn_route_map_match.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if route-map match by EVPN route-type works. +""" + +import os +import re +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 +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("c1", "r1"), "s2": ("r1", "r2"), "s3": ("r2", "c2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + tgen.net["r1"].cmd( + """ +ip link add vxlan10 type vxlan id 10 dstport 4789 local 10.10.10.1 nolearning +ip link add name br10 type bridge +ip link set dev vxlan10 master br10 +ip link set dev r1-eth0 master br10 +ip link set up dev br10 +ip link set up dev vxlan10""" + ) + + tgen.net["r2"].cmd( + """ +ip link add vxlan10 type vxlan id 10 dstport 4789 local 10.10.10.2 nolearning +ip link add name br10 type bridge +ip link set dev vxlan10 master br10 +ip link set dev r2-eth1 master br10 +ip link set up dev br10 +ip link set up dev vxlan10""" + ) + + 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_bgp_evpn_route_map_match_route_type(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads( + r1.vtysh_cmd( + "show bgp l2vpn evpn neighbor 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.1:1": { + "[5]:[0]:[32]:[10.10.10.10]": { + "valid": True, + } + }, + "10.10.10.2:2": { + "[3]:[0]:[32]:[10.10.10.2]": { + "valid": True, + } + }, + }, + "totalPrefixCounter": 2, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Filtered EVPN routes should not be advertised" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py index 7e4bcc8..d9177b4 100644 --- a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -132,6 +132,7 @@ def setup_module(mod): for rname, router in router_list.items(): if rname == "r1": + router.load_config(TopoRouter.RD_MGMTD, None, "--vrfwnetns") router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.after.json b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.after.json new file mode 100644 index 0000000..0b5b1a1 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.after.json @@ -0,0 +1 @@ +{"27.3.0.85:3":{"rd":"27.3.0.85:3","[5]:[0]:[16]:[10.27.0.0]":{"prefix":"[5]:[0]:[16]:[10.27.0.0]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":16,"ip":"10.27.0.0","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]}},"30.0.0.3:2":{"rd":"30.0.0.3:2","[5]:[0]:[32]:[4.4.4.1]":{"prefix":"[5]:[0]:[32]:[4.4.4.1]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"internal","routeType":5,"ethTag":0,"ipLen":32,"ip":"4.4.4.1","metric":0,"locPrf":100,"weight":0,"peerId":"10.30.30.30","path":"","origin":"incomplete","extendedCommunity":{"string":"RT:65000:300 ET:8 Rmac:44:20:00:ff:ff:01"},"nexthops":[{"ip":"10.30.30.30","hostname":"PE2","afi":"ipv4","used":true}]}]]}},"numPrefix":2,"numPaths":2} diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.before.json b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.before.json new file mode 100644 index 0000000..67417b5 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.before.json @@ -0,0 +1 @@ +{"27.3.0.85:3":{"rd":"27.3.0.85:3","[5]:[0]:[24]:[10.27.7.0]":{"prefix":"[5]:[0]:[24]:[10.27.7.0]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":24,"ip":"10.27.7.0","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]},"[5]:[0]:[30]:[10.27.7.8]":{"prefix":"[5]:[0]:[30]:[10.27.7.8]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":30,"ip":"10.27.7.8","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]},"[5]:[0]:[32]:[10.27.7.22]":{"prefix":"[5]:[0]:[32]:[10.27.7.22]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":32,"ip":"10.27.7.22","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]}},"30.0.0.3:2":{"rd":"30.0.0.3:2","[5]:[0]:[32]:[4.4.4.1]":{"prefix":"[5]:[0]:[32]:[4.4.4.1]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"internal","routeType":5,"ethTag":0,"ipLen":32,"ip":"4.4.4.1","metric":0,"locPrf":100,"weight":0,"peerId":"10.30.30.30","path":"","origin":"incomplete","extendedCommunity":{"string":"RT:65000:300 ET:8 Rmac:44:20:00:ff:ff:01"},"nexthops":[{"ip":"10.30.30.30","hostname":"PE2","afi":"ipv4","used":true}]}]]}},"numPrefix":4,"numPaths":4} diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf index 9fb2bd6..b58219a 100644 --- a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf @@ -11,9 +11,22 @@ router bgp 65000 advertise-svi-ip ! router bgp 65000 vrf vrf-red + address-family ipv4 unicast + redistribute static + exit-address-family ! address-family l2vpn evpn route-target import *:300 route-target import auto exit-address-family ! +router bgp 65000 vrf vrf-purple + address-family ipv4 unicast + redistribute static + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf index 8c6cf3e..cea60a8 100644 --- a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf @@ -5,6 +5,12 @@ vrf vrf-red vni 100 exit-vrf ! +vrf vrf-purple + vni 4000 + ip route 10.27.7.0/24 blackhole + ip route 10.27.7.22/32 blackhole + ip route 10.27.7.10/30 blackhole +exit-vrf ! interface lo ip address 10.10.10.10/32 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 65c0c35..fd75c34 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 @@ -91,6 +91,10 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe.run("ip link add name bridge type bridge stp_state 0") pe.run("ip link set dev bridge type bridge vlan_filtering 1") pe.run("bridge vlan add vid 1 dev bridge self") + if pe_name == "PE1": + pe.run("ip link set dev bridge address 44:00:00:ff:ff:01") + if pe_name == "PE2": + pe.run("ip link set dev bridge address 44:20:00:ff:ff:01") pe.run("ip link set dev bridge up") # setup svi @@ -113,6 +117,18 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe.run("bridge vlan add dev vxlan0 vid 1 tunnel_info id 101") pe.run("ip link set up dev vxlan0") + # VRF creation + pe.run("ip link add vrf-purple type vrf table 1003") + pe.run("ip link set dev vrf-purple up") + if pe_name == "PE1": + pe.run("ip link add vrf-blue type vrf table 1002") + pe.run("ip link set dev vrf-blue up") + pe.run("ip addr add 27.2.0.85/32 dev vrf-blue") + pe.run("ip addr add 27.3.0.85/32 dev vrf-purple") + if pe_name == "PE2": + pe.run("ip link add vrf-blue type vrf table 2400") + pe.run("ip link set dev vrf-blue up") + # setup PE interface pe.run("ip link set dev {0}-{1} master bridge".format(pe_name, intf)) pe.run("bridge vlan del vid 1 dev {0}-{1}".format(pe_name, intf)) @@ -120,7 +136,7 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe.run("bridge vlan add vid 1 dev {0}-{1}".format(pe_name, intf)) pe.run("bridge vlan add vid 1 pvid untagged dev {0}-{1}".format(pe_name, intf)) - # l3vni 100 + # L3VNI 100 pe.run("ip link add vrf-red type vrf table 1400") pe.run("ip link add link bridge name vlan100 type vlan id 100 protocol 802.1q") pe.run("ip link set dev vlan100 master vrf-blue") @@ -129,9 +145,16 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe.run("bridge vlan add dev vxlan0 vid 100") pe.run("bridge vlan add dev vxlan0 vid 100 tunnel_info id 100") + # L3VNI 4000 + pe.run("ip link add link bridge name vlan400 type vlan id 400 protocol 802.1q") + pe.run("ip link set dev vlan400 master vrf-purple") + pe.run("ip link set dev vlan400 up") + pe.run("bridge vlan add vid 400 dev bridge self") + pe.run("bridge vlan add dev vxlan0 vid 400") + pe.run("bridge vlan add dev vxlan0 vid 400 tunnel_info id 4000") + # add a vrf for testing DVNI if pe_name == "PE2": - pe.run("ip link add vrf-blue type vrf table 2400") pe.run("ip link add link bridge name vlan300 type vlan id 300 protocol 802.1q") pe.run("ip link set dev vlan300 master vrf-blue") pe.run("ip link set dev vlan300 up") @@ -199,6 +222,14 @@ def show_vni_json_elide_ifindex(pe, vni, expected): return topotest.json_cmp(output_json, expected) +def show_bgp_l2vpn_evpn_route_type_prefix_json(pe, expected): + output_json = pe.vtysh_cmd( + "show bgp l2vpn evpn route type prefix json", isjson=True + ) + + return topotest.json_cmp(output_json, expected) + + def check_vni_macs_present(tgen, router, vni, maclist): result = router.vtysh_cmd("show evpn mac vni {} json".format(vni), isjson=True) for rname, ifname in maclist: @@ -331,6 +362,7 @@ def mac_test_local_remote(local, remote): remote_output_json = json.loads(remote_output) local_output_vni_json = json.loads(local_output_vni) + # breakpoint() for vni in local_output_json: if vni not in remote_output_json: continue @@ -542,6 +574,42 @@ def test_dvni(): # tgen.mininet_cli() +def test_pe_advertise_aggr_evpn_route(): + "BGP advertise aggregated Type-5 prefix route" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking BGP EVPN route contains non-aggregate prefixes") + pe1 = tgen.gears["PE1"] + json_file = "{}/{}/bgp.evpn.route.prefix.before.json".format(CWD, pe1.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_bgp_l2vpn_evpn_route_type_prefix_json, pe1, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + assert result is None, assertmsg + + logger.info("Configure BGP aggregate-address summary-only under ipv4-unicast") + pe1.cmd( + 'vtysh -c "config t" -c "router bgp 65000 vrf vrf-purple" ' + + ' -c "address-family ipv4 unicast" ' + + ' -c "aggregate-address 10.27.0.0/16 summary-only" ' + ) + + logger.info("Checking BGP EVPN route contains aggregated prefix") + pe1 = tgen.gears["PE1"] + json_file = "{}/{}/bgp.evpn.route.prefix.after.json".format(CWD, pe1.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_bgp_l2vpn_evpn_route_type_prefix_json, pe1, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + assert result is None, assertmsg + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/bgp_features/r1/ospf6d.conf b/tests/topotests/bgp_features/r1/ospf6d.conf index 9afc6f4..3e6196e 100644 --- a/tests/topotests/bgp_features/r1/ospf6d.conf +++ b/tests/topotests/bgp_features/r1/ospf6d.conf @@ -3,19 +3,19 @@ log file ospf6d.log ! debug ospf6 neighbor ! interface r1-lo + ipv6 ospf6 area 0.0.0.0 ! interface r1-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 10 ! interface r1-eth2 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 10 ! router ospf6 ospf6 router-id 192.168.0.1 log-adjacency-changes - interface r1-lo area 0.0.0.0 - interface r1-eth1 area 0.0.0.0 - interface r1-eth2 area 0.0.0.0 ! line vty ! diff --git a/tests/topotests/bgp_features/r2/ospf6d.conf b/tests/topotests/bgp_features/r2/ospf6d.conf index 7fe5356..56aecd0 100644 --- a/tests/topotests/bgp_features/r2/ospf6d.conf +++ b/tests/topotests/bgp_features/r2/ospf6d.conf @@ -3,19 +3,19 @@ log file ospf6d.log ! debug ospf6 neighbor ! interface r2-lo + ipv6 ospf6 area 0.0.0.0 ! interface r2-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 5 ! interface r2-eth2 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 10 ! router ospf6 ospf6 router-id 192.168.0.2 log-adjacency-changes - interface r2-lo area 0.0.0.0 - interface r2-eth1 area 0.0.0.0 - interface r2-eth2 area 0.0.0.0 ! line vty ! diff --git a/tests/topotests/bgp_features/r3/ospf6d.conf b/tests/topotests/bgp_features/r3/ospf6d.conf index 07325b6..f15b9d9 100644 --- a/tests/topotests/bgp_features/r3/ospf6d.conf +++ b/tests/topotests/bgp_features/r3/ospf6d.conf @@ -3,19 +3,19 @@ log file ospf6d.log ! debug ospf6 neighbor ! interface r3-lo + ipv6 ospf6 area 0.0.0.0 ! interface r3-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 5 ! interface r3-eth2 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 5 ! router ospf6 ospf6 router-id 192.168.0.3 log-adjacency-changes - interface r3-lo area 0.0.0.0 - interface r3-eth1 area 0.0.0.0 - interface r3-eth2 area 0.0.0.0 ! line vty ! diff --git a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py index a2be859..57aeea8 100644 --- a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py +++ b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py @@ -51,7 +51,6 @@ sys.path.append(os.path.join(CWD, "../")) from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger -from lib.common_config import generate_support_bundle # Required to instantiate the topology builder class. @@ -140,10 +139,7 @@ def test_bgp_convergence(): ) _, res = topotest.run_and_expect(test_func, None, count=210, wait=1) assertmsg = "BGP router network did not converge" - if res is not None: - generate_support_bundle() - assert res is None, assertmsg - generate_support_bundle() + assert res is None, assertmsg def test_bgp_flowspec(): diff --git a/tests/topotests/bgp_l3vpn_label_export/__init__.py b/tests/topotests/bgp_l3vpn_label_export/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/__init__.py diff --git a/tests/topotests/bgp_l3vpn_label_export/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_label_export/r1/bgpd.conf new file mode 100644 index 0000000..bb1ed4c --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r1/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65001 + bgp router-id 192.0.2.1 + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + neighbor 192.0.2.2 remote-as 65002 + neighbor 192.0.2.2 timers 1 3 + neighbor 192.0.2.2 timers connect 1 + neighbor 192.0.2.2 ebgp-multihop 2 + address-family ipv4 vpn + neighbor 192.0.2.2 activate + exit-address-family +! +router bgp 65001 vrf vrf1 + address-family ipv4 unicast + redistribute connected + label vpn export 1111 + rd vpn export 101:1 + rt vpn both 52:100 + import vpn + export vpn + exit-address-family +! diff --git a/tests/topotests/bgp_l3vpn_label_export/r1/ldpd.conf b/tests/topotests/bgp_l3vpn_label_export/r1/ldpd.conf new file mode 100644 index 0000000..04ae068 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r1/ldpd.conf @@ -0,0 +1,26 @@ +hostname r1 +log file ldpd.log +password zebra +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 192.0.2.1 + ! + address-family ipv4 + discovery transport-address 192.0.2.1 + ! + interface r1-eth0 + ! + interface r1-eth1 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_label_export/r1/staticd.conf b/tests/topotests/bgp_l3vpn_label_export/r1/staticd.conf new file mode 100644 index 0000000..7f2f057 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r1/staticd.conf @@ -0,0 +1 @@ +ip route 192.0.2.2/32 192.168.1.2 diff --git a/tests/topotests/bgp_l3vpn_label_export/r1/zebra.conf b/tests/topotests/bgp_l3vpn_label_export/r1/zebra.conf new file mode 100644 index 0000000..7bdacb1 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 192.0.2.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_l3vpn_label_export/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_label_export/r2/bgpd.conf new file mode 100644 index 0000000..18a11cf --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r2/bgpd.conf @@ -0,0 +1,23 @@ +router bgp 65002 + bgp router-id 192.0.2.2 + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as 65001 + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.1.1 ebgp-multihop 2 + neighbor 192.168.1.1 update-source 192.0.2.2 + address-family ipv4 vpn + neighbor 192.168.1.1 activate + exit-address-family +! +router bgp 65002 vrf vrf1 + address-family ipv4 unicast + redistribute connected + label vpn export 2222 + rd vpn export 102:1 + rt vpn both 52:100 + import vpn + export vpn + exit-address-family +! diff --git a/tests/topotests/bgp_l3vpn_label_export/r2/ldpd.conf b/tests/topotests/bgp_l3vpn_label_export/r2/ldpd.conf new file mode 100644 index 0000000..f4307f1 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r2/ldpd.conf @@ -0,0 +1,24 @@ +hostname r2 +log file ldpd.log +password zebra +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 192.0.2.2 + ! + address-family ipv4 + discovery transport-address 192.0.2.2 + ! + interface r2-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_label_export/r2/staticd.conf b/tests/topotests/bgp_l3vpn_label_export/r2/staticd.conf new file mode 100644 index 0000000..e3f5d7d --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r2/staticd.conf @@ -0,0 +1 @@ +ip route 192.0.2.1/32 192.168.1.1 diff --git a/tests/topotests/bgp_l3vpn_label_export/r2/zebra.conf b/tests/topotests/bgp_l3vpn_label_export/r2/zebra.conf new file mode 100644 index 0000000..40dfa98 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r2/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 192.0.2.2/32 +! +interface r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_l3vpn_label_export/test_bgp_l3vpn_label_export.py b/tests/topotests/bgp_l3vpn_label_export/test_bgp_l3vpn_label_export.py new file mode 100644 index 0000000..7c23a3e --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/test_bgp_l3vpn_label_export.py @@ -0,0 +1,587 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by Louis Scalbert <louis.scalbert@6wind.com> +# Copyright 2023 6WIND S.A. +# + +""" + +""" + +import os +import re +import sys +import json +import pytest +import functools + +from copy import deepcopy + +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 kill_router_daemons, start_router_daemons, step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for rtr in [1, 2]: + tgen.add_router("r{}".format(rtr)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rtr in [1, 2]: + tgen.gears["r{}".format(rtr)].cmd("ip link add vrf1 type vrf table 10") + tgen.gears["r{}".format(rtr)].cmd("ip link set vrf1 up") + tgen.gears["r{}".format(rtr)].cmd( + "ip address add dev vrf1 192.0.3.{}/32".format(rtr) + ) + tgen.gears["r{}".format(rtr)].run( + "sysctl -w net.mpls.conf.r{}-eth0.input=1".format(rtr) + ) + tgen.gears["r{}".format(rtr)].run("sysctl -w net.mpls.conf.vrf1.input=1") + + 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_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def check_bgp_vpn_prefix(label, rname="r1", rd=None): + tgen = get_topogen() + + if rd: + output = json.loads( + tgen.gears[rname].vtysh_cmd( + "show bgp ipv4 vpn rd {} 192.0.3.2/32 json".format(rd) + ) + ) + else: + output = json.loads( + tgen.gears[rname].vtysh_cmd( + "show bgp vrf vrf1 ipv4 unicast 192.0.3.2/32 json" + ) + ) + + if label == "auto": + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "65002"}, + "nexthops": [{"ip": "192.0.2.2"}], + }, + ] + } + elif label and not rd: + expected = { + "paths": [ + { + "valid": True, + "remoteLabel": label, + "aspath": {"string": "65002"}, + "nexthops": [{"ip": "192.0.2.2"}], + }, + ] + } + elif label and rd: + expected = { + "102:1": { + "prefix": "192.0.3.2/32", + "paths": [ + { + "valid": True, + "remoteLabel": label, + "nexthops": [{"ip": "0.0.0.0"}], + } + ], + } + } + else: + expected = {} + + return topotest.json_cmp(output, expected, exact=(label is None)) + + +def check_mpls_table(label, protocol): + tgen = get_topogen() + + if label == "auto": + cmd = "show mpls table json" + else: + cmd = "show mpls table {} json".format(label) + + output = json.loads(tgen.gears["r2"].vtysh_cmd(cmd)) + + if label == "auto" and protocol: + output_copy = deepcopy(output) + for key, data in output_copy.items(): + for nexthop in data.get("nexthops", []): + if nexthop.get("type", None) != protocol: + continue + output = data + break + + if protocol: + expected = { + "nexthops": [ + { + "type": protocol, + }, + ] + } + else: + expected = {} + + return topotest.json_cmp(output, expected, exact=(protocol is None)) + + +def check_mpls_ldp_binding(): + tgen = get_topogen() + + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show mpls ldp binding 192.0.2.2/32 json") + ) + expected = { + "bindings": [ + { + "prefix": "192.0.2.2/32", + "localLabel": "16", # first available label + "inUse": 1, + }, + ] + } + + return topotest.json_cmp(output, expected) + + +def test_convergence(): + "Test protocol convergence" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Check BGP and LDP convergence") + test_func = functools.partial(check_bgp_vpn_prefix, 2222) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefix on R1" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, 2222, "BGP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: [2222/2222]" in output, "Failed to see BGP label chunk" + + +def test_vpn_label_export_16(): + "Test that assigning the label value of 16 is not possible because it used by LDP" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export 16" + ) + + step("Check that label vpn export 16 fails") + test_func = functools.partial(check_bgp_vpn_prefix, None) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP prefix on R1" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, 2222, None) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp" not in output, "Unexpected BGP label chunk" + + +def test_vpn_label_export_2222(): + "Test that setting back the label value of 2222 works" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export 2222" + ) + + step("Check that label vpn export 2222 is OK") + test_func = functools.partial(check_bgp_vpn_prefix, 2222) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefix on R1" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, "auto", "BGP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: [2222/2222]" in output, "Failed to see BGP label chunk" + + +def test_vpn_label_export_auto(): + "Test that setting label vpn export auto works" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export auto" + ) + + step("Check that label vpn export auto is OK") + test_func = functools.partial(check_bgp_vpn_prefix, "auto") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefix on R1" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, "auto", "BGP") + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Failed to see BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " in output, "Failed to see BGP label chunk" + + +def test_vpn_label_export_no_auto(): + "Test that UNsetting label vpn export auto removes the prefix from R1 table and R2 LDP table" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv4 unicast 192.0.3.2/32 json") + ) + + auto_label = output.get("paths")[0].get("remoteLabel", None) + assert auto_label is not None, "Failed to fetch prefix label on R1" + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "no label vpn export auto" + ) + + step("Check that no label vpn export auto is OK") + test_func = functools.partial(check_bgp_vpn_prefix, 3, rname="r2", rd="102:1") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP prefix on R2" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, auto_label, None) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Unexpected BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " not in output, "Unexpected BGP label chunk" + + +def test_vpn_label_export_auto_back(): + "Test that setting back label vpn export auto works" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + output = json.loads( + tgen.gears["r2"].vtysh_cmd("show bgp vrf vrf1 ipv4 unicast 192.0.3.2/32 json") + ) + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export auto" + ) + + step("Check that label vpn export auto is OK") + test_func = functools.partial(check_bgp_vpn_prefix, "auto") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefix on R1" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, "auto", "BGP") + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Failed to see BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " in output, "Failed to see BGP label chunk" + + +def test_vpn_label_export_manual_from_auto(): + "Test that setting a manual label value from the BGP chunk range works" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv4 unicast 192.0.3.2/32 json") + ) + + auto_label = output.get("paths")[0].get("remoteLabel", None) + assert auto_label is not None, "Failed to fetch prefix label on R1" + + auto_label = auto_label + 1 + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export {}".format(auto_label) + ) + + step("Check that label vpn export {} is OK".format(auto_label)) + test_func = functools.partial( + check_bgp_vpn_prefix, auto_label, rname="r2", rd="102:1" + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefix on R2" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, auto_label, "BGP") + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Failed to see BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " in output, "Failed to see BGP label chunk" + + +def test_vpn_label_configure_dynamic_range(): + "Test that if a dynamic range is configured, then the next dynamic allocations will be done in that block" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + tgen.gears["r2"].vtysh_cmd("conf\n" "mpls label dynamic-block 500 1000\n") + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export auto" + ) + step("Check that label vpn export auto starting at 500 is OK") + test_func = functools.partial(check_bgp_vpn_prefix, 500, rname="r2", rd="102:1") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP prefix on R2" + + test_func = functools.partial(check_mpls_table, 500, "BGP") + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Unexpected BGP label on R2" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " in output, "Failed to see BGP label chunk" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) + + +def test_vpn_label_restart_ldp(): + "Test that if a dynamic range is configured, then when LDP restarts, it follows the new dynamic range" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_list = tgen.routers() + + step("Kill LDP on R2") + kill_router_daemons(tgen, "r2", ["ldpd"]) + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto ldp: " not in output, "Unexpected LDP label chunk" + + step("Bring up LDP on R2") + + start_router_daemons(tgen, "r2", ["ldpd"]) + + test_func = functools.partial(check_mpls_table, 628, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto ldp: [628/691]" in output, "Failed to see LDP label chunk [628/691]" + assert "Proto ldp: [692/755]" in output, "Failed to see LDP label chunk [692/755]" + + +def test_vpn_label_unconfigure_dynamic_range(): + "Test that if the dynamic range is unconfigured, then the next dynamic allocations will be done at the first free place." + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + tgen.gears["r2"].vtysh_cmd("conf\n" "no mpls label dynamic-block 500 1000\n") + step("Check that unconfiguring label vpn export auto will remove BGP label chunk") + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "no label vpn export auto" + ) + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " not in output, "Unexpected BGP label chunk" + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export auto" + ) + step("Check that label vpn export auto starting at 16 is OK") + test_func = functools.partial(check_bgp_vpn_prefix, 16, rname="r2", rd="102:1") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP prefix on R2" + + test_func = functools.partial(check_mpls_table, 16, "BGP") + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Unexpected BGP label on R2" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " in output, "Failed to see BGP label chunk" diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py index 0ac5350..45c44ba 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py @@ -161,6 +161,17 @@ def ltemplatePreRouterStartHook(): logger.info( "setup {0} vrf {0}-cust1, {0}-eth4. enabled mpls input.".format(rtr) ) + # configure cust4 VRFs & MPLS + cmds = [ + "ip link add {0}-cust4 type vrf table 30", + "ip link set dev {0}-cust4 up", + "ip link add {0}-cust5 type vrf table 40", + "ip link set dev {0}-cust5 up", + ] + rtr = "r1" + for cmd in cmds: + cc.doCmd(tgen, rtr, cmd.format(rtr)) + logger.info("setup {0} vrf {0}-cust3 and{0}-cust4.".format(rtr)) # configure cust2 VRFs & MPLS rtrs = ["r4"] cmds = [ diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf index 72211fe..0d652da 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf @@ -50,6 +50,31 @@ router bgp 5227 vrf r1-cust1 export vpn exit-address-family +router bgp 5227 vrf r1-cust4 + no bgp network import-check -! -end + bgp router-id 192.168.1.1 + + address-family ipv4 unicast + network 172.16.0.0/24 + + rd vpn export 10:14 + rt vpn export 52:100 + + import vpn + export vpn + exit-address-family + +router bgp 5227 vrf r1-cust5 + bgp router-id 192.168.1.1 + + address-family ipv4 unicast + network 172.16.1.1/32 + + label vpn export 105 + rd vpn export 10:15 + rt vpn both 52:100 + + import vpn + export vpn + exit-address-family diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf index 221bc7a..9a5b0a6 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf @@ -18,6 +18,10 @@ interface r1-eth4 ip address 192.168.1.1/24 no link-detect +interface r1-cust5 + ip address 172.16.1.1/32 + no link-detect + ip forwarding diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py index 1e2758c..217657d 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py @@ -59,12 +59,28 @@ want_r1_cust1_routes = [ {"p": "5.1.1.0/24", "n": "99.0.0.1"}, {"p": "6.0.1.0/24", "n": "99.0.0.1"}, {"p": "6.0.2.0/24", "n": "99.0.0.1"}, + {"p": "172.16.0.0/24", "n": "0.0.0.0", "bp": True}, + {"p": "172.16.1.1/32", "n": "0.0.0.0", "bp": True}, {"p": "99.0.0.1/32", "n": "192.168.1.2"}, ] bgpribRequireUnicastRoutes( "r1", "ipv4", "r1-cust1", "Customer 1 routes in r1 vrf", want_r1_cust1_routes ) +want_r1_cust4_routes = [ + {"p": "172.16.0.0/24", "n": "0.0.0.0", "bp": True}, +] +bgpribRequireUnicastRoutes( + "r1", "ipv4", "r1-cust4", "Customer 4 routes in r1 vrf", want_r1_cust4_routes +) + +want_r1_cust5_routes = [ + {"p": "172.16.1.1/32", "n": "0.0.0.0", "bp": True}, +] +bgpribRequireUnicastRoutes( + "r1", "ipv4", "r1-cust5", "Customer 5 routes in r1 vrf", want_r1_cust5_routes +) + want_r3_cust1_routes = [ {"p": "5.1.0.0/24", "n": "99.0.0.2"}, {"p": "5.1.1.0/24", "n": "99.0.0.2"}, @@ -667,7 +683,7 @@ bgpribRequireUnicastRoutes( luCommand( "ce1", 'vtysh -c "show bgp ipv4 uni"', - "12 routes and 12", + "14 routes and 14", "wait", "Local and remote routes", 10, @@ -689,7 +705,7 @@ bgpribRequireUnicastRoutes( luCommand( "ce2", 'vtysh -c "show bgp ipv4 uni"', - "12 routes and 15", + "14 routes and 17", "wait", "Local and remote routes", 10, @@ -721,7 +737,7 @@ luCommand("r4", 'vtysh -c "show ip route vrf r4-cust2"') luCommand( "ce3", 'vtysh -c "show bgp ipv4 uni"', - "12 routes and 13", + "14 routes and 15", "wait", "Local and remote routes", 10, @@ -743,7 +759,7 @@ bgpribRequireUnicastRoutes( luCommand( "ce4", 'vtysh -c "show bgp vrf ce4-cust2 ipv4 uni"', - "12 routes and 14", + "14 routes and 16", "wait", "Local and remote routes", 10, @@ -875,4 +891,11 @@ luCommand( "pass", "Redundant route 2 details", ) +luCommand( + "r1", + 'vtysh -c "show ip route vrf r1-cust5 5.1.0.0/24"', + "Known via .bgp., distance 200, .* vrf r1-cust5, best", + "pass", + "Recursive route leak details", +) # done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py index 36be926..190879c 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py @@ -49,7 +49,7 @@ if ret != False and found != None: luCommand( rtr, 'vtysh -c "show bgp ipv4 uni" | grep Display', - " 12 route", + " 14 route", "wait", "BGP routes removed", wait, 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 46993c7..e05bf21 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 @@ -226,7 +226,7 @@ else: ave_b = float(delta_b) / float(num) luCommand( rtr, - 'vtysh -c "show thread cpu"', + 'vtysh -c "show event cpu"', ".", "pass", "BGPd heap: {0} {1} --> {2} {3} ({4} {1}/vpn route)".format( @@ -239,7 +239,7 @@ else: ) luCommand( rtr, - 'vtysh -c "show thread cpu"', + 'vtysh -c "show event cpu"', ".", "pass", "Zebra heap: {0} {1} --> {2} {3} ({4} {1}/vpn route)".format( diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json index 3c02e26..bb50bc9 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json @@ -7,12 +7,12 @@ { "fib":true, "ip":"11.1.1.6", - "weight":25 + "weight":85 }, { "fib":true, "ip":"11.1.1.2", - "weight":75 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json index 3c2d42c..57726ce 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json @@ -7,12 +7,12 @@ { "fib":true, "ip":"11.1.1.6", - "weight":33 + "weight":127 }, { "fib":true, "ip":"11.1.1.2", - "weight":66 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json index 3d80018..f2368df 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json @@ -7,12 +7,12 @@ { "fib":true, "ip":"11.1.1.6", - "weight":33 + "weight":127 }, { "fib":true, "ip":"11.1.1.2", - "weight":66 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json index 6ed3f8e..7b4da2a 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json @@ -7,7 +7,7 @@ { "fib":true, "ip":"11.1.1.2", - "weight":100 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json index 95531d9..3062d1c 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json @@ -7,7 +7,7 @@ { "fib":true, "ip":"11.1.1.2", - "weight":100 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json index beac501..662b7f7 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json @@ -12,7 +12,7 @@ { "fib":true, "ip":"11.1.1.2", - "weight":100 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json index eb27ce2..d9b5a89 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json @@ -12,7 +12,7 @@ { "fib":true, "ip":"11.1.1.2", - "weight":100 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json index 7e2fa6b..53be117 100644 --- a/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json +++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json @@ -7,12 +7,12 @@ { "fib":true, "ip":"11.1.2.6", - "weight":33 + "weight":127 }, { "fib":true, "ip":"11.1.2.2", - "weight":66 + "weight":255 } ] } diff --git a/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json index d35e4ef..17b9acc 100644 --- a/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json +++ b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json @@ -2,5 +2,5 @@ "ledger":0, "inUse":0, "requests":0, - "labelChunks":1 + "labelChunks":0 } diff --git a/tests/topotests/bgp_multiview_topo1/exabgp.env b/tests/topotests/bgp_multiview_topo1/exabgp.env index a328e04..ec978c6 100644 --- a/tests/topotests/bgp_multiview_topo1/exabgp.env +++ b/tests/topotests/bgp_multiview_topo1/exabgp.env @@ -1,5 +1,6 @@ [exabgp.api] +ack = false encoder = text highres = false respawn = false diff --git a/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg index 20e71c8..0303230 100644 --- a/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 1 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 1"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 1 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.1; - local-address 172.16.1.1; - local-as 65001; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 1; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.1; + local-address 172.16.1.1; + local-as 65001; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg index 1e8eef1..13670c3 100644 --- a/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 2 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 2"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 2 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.2; - local-address 172.16.1.2; - local-as 65002; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 2; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.2; + local-address 172.16.1.2; + local-as 65002; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg index ef1b249..0afc484 100644 --- a/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 3 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 3"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 3 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.3; - local-address 172.16.1.3; - local-as 65003; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 3; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.3; + local-address 172.16.1.3; + local-as 65003; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg index 7c50f73..0f5b536 100644 --- a/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 4 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 4"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 4 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.4; - local-address 172.16.1.4; - local-as 65004; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 4; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.4; + local-address 172.16.1.4; + local-as 65004; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg index 22163c7..365aa6b 100644 --- a/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 5 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 5"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 5 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.5; - local-address 172.16.1.5; - local-as 65005; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 5; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.5; + local-address 172.16.1.5; + local-as 65005; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg index 40b54c3..1038033 100644 --- a/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 6 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 6"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 6 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.6; - local-address 172.16.1.6; - local-as 65006; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 6; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.6; + local-address 172.16.1.6; + local-as 65006; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg index 33312d0..7411338 100644 --- a/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 7 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 7"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 7 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.7; - local-address 172.16.1.7; - local-as 65007; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 7; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.7; + local-address 172.16.1.7; + local-as 65007; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg index 173ccb9..17a4e49 100644 --- a/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 8 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 8"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 8 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.8; - local-address 172.16.1.8; - local-as 65008; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 8; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.8; + local-address 172.16.1.8; + local-as 65008; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_oad/__init__.py b/tests/topotests/bgp_oad/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/topotests/bgp_oad/__init__.py diff --git a/tests/topotests/bgp_oad/r1/frr.conf b/tests/topotests/bgp_oad/r1/frr.conf new file mode 100644 index 0000000..39045ba --- /dev/null +++ b/tests/topotests/bgp_oad/r1/frr.conf @@ -0,0 +1,21 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.1.2 oad + neighbor 192.168.1.4 remote-as external + neighbor 192.168.1.4 timers 1 3 + neighbor 192.168.1.4 timers connect 1 + address-family ipv4 unicast + neighbor 192.168.1.4 route-map r4 in + exit-address-family +! +route-map r4 permit 10 + set local-preference 123 + set metric 123 +exit diff --git a/tests/topotests/bgp_oad/r2/frr.conf b/tests/topotests/bgp_oad/r2/frr.conf new file mode 100644 index 0000000..fdd23f3 --- /dev/null +++ b/tests/topotests/bgp_oad/r2/frr.conf @@ -0,0 +1,18 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! +int r2-eth1 + ip address 192.168.2.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.1.1 oad + neighbor 192.168.2.3 remote-as external + neighbor 192.168.2.3 timers 1 3 + neighbor 192.168.2.3 timers connect 1 + neighbor 192.168.2.3 oad +! diff --git a/tests/topotests/bgp_oad/r3/frr.conf b/tests/topotests/bgp_oad/r3/frr.conf new file mode 100644 index 0000000..02dd5ad --- /dev/null +++ b/tests/topotests/bgp_oad/r3/frr.conf @@ -0,0 +1,22 @@ +! +int lo + ip address 10.10.10.10/32 +! +int r3-eth0 + ip address 192.168.2.3/24 +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + neighbor 192.168.2.2 oad + ! + address-family ipv4 unicast + redistribute connected route-map connected + exit-address-family +! +route-map connected permit 10 + set local-preference 123 + set metric 123 +! diff --git a/tests/topotests/bgp_oad/r4/frr.conf b/tests/topotests/bgp_oad/r4/frr.conf new file mode 100644 index 0000000..83dcf39 --- /dev/null +++ b/tests/topotests/bgp_oad/r4/frr.conf @@ -0,0 +1,16 @@ +! +int r4-eth0 + ip address 192.168.1.4/24 +! +int r4-eth1 + ip address 192.168.4.4/24 +! +router bgp 65004 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.4.5 remote-as external + neighbor 192.168.4.5 timers 1 3 + neighbor 192.168.4.5 timers connect 1 +! diff --git a/tests/topotests/bgp_oad/r5/frr.conf b/tests/topotests/bgp_oad/r5/frr.conf new file mode 100644 index 0000000..f8e1609 --- /dev/null +++ b/tests/topotests/bgp_oad/r5/frr.conf @@ -0,0 +1,17 @@ +! +int lo + ip address 10.10.10.10/32 +! +int r5-eth0 + ip address 192.168.4.5/24 +! +router bgp 65005 + no bgp ebgp-requires-policy + neighbor 192.168.4.4 remote-as external + neighbor 192.168.4.4 timers 1 3 + neighbor 192.168.4.4 timers connect 1 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_oad/test_bgp_oad.py b/tests/topotests/bgp_oad/test_bgp_oad.py new file mode 100644 index 0000000..b26c548 --- /dev/null +++ b/tests/topotests/bgp_oad/test_bgp_oad.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if local-preference is passed between different EBGP peers when +EBGP-OAD is configured. +""" + +import os +import re +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 +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2", "r4"), "s2": ("r2", "r3"), "s3": ("r4", "r5")} + 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_bgp_dynamic_capability_role(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.10.10.10/32 json")) + expected = { + "paths": [ + { + "aspath": {"string": "65002 65003"}, + "metric": 123, + "locPrf": 123, + "peer": { + "hostname": "r2", + "type": "external (oad)", + }, + }, + { + "aspath": {"string": "65004 65005"}, + "metric": 123, + "locPrf": 123, + "bestpath": {"selectionReason": "Peer Type"}, + "peer": { + "hostname": "r4", + "type": "external", + }, + }, + ] + } + 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, "Can't converge" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env b/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env index 6c554f5..989228a 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env +++ b/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env @@ -1,5 +1,6 @@ [exabgp.api] +ack = false encoder = text highres = false respawn = false @@ -43,7 +44,7 @@ enable = false file = '' [exabgp.reactor] -speed = 1.0 +speed = 5.0 [exabgp.tcp] acl = false diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py index 0f998c1..6c1f8b0 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 "Helper script to read api commands from a pipe and feed them to ExaBGP" import sys diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg index 4a7dc48..e6606d2 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg @@ -1,21 +1,17 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in"; - encoder text; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 1"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.2; - local-address 10.0.1.2; - local-as 64510; - peer-as 64510; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 1; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.2; + local-address 10.0.1.2; + local-as 64510; + peer-as 64510; + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py index 0f998c1..6c1f8b0 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 "Helper script to read api commands from a pipe and feed them to ExaBGP" import sys diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg index b53b054..6a6ba0b 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg @@ -1,21 +1,17 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in"; - encoder text; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 2"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.2; - local-address 10.0.2.2; - local-as 64511; - peer-as 64511; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 2; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.2; + local-address 10.0.2.2; + local-as 64511; + peer-as 64511; + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py index 0f998c1..6c1f8b0 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 "Helper script to read api commands from a pipe and feed them to ExaBGP" import sys diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg index 6a1cc2f..9714b1f 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg @@ -1,21 +1,17 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in"; - encoder text; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 3"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.2; - local-address 10.0.3.2; - local-as 64502; - peer-as 64501; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 3; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.2; + local-address 10.0.3.2; + local-as 64502; + peer-as 64501; + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py index 0f998c1..6c1f8b0 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 "Helper script to read api commands from a pipe and feed them to ExaBGP" import sys diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg index 2cc26cb..8c38a88 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg @@ -1,21 +1,17 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in"; - encoder text; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 4"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.2; - local-address 10.0.4.2; - local-as 64503; - peer-as 64501; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 4; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.2; + local-address 10.0.4.2; + local-as 64503; + peer-as 64501; + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py b/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py index ad6674c..9239be9 100755 --- a/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py +++ b/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py @@ -62,6 +62,19 @@ from lib.topolog import logger pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] +# Prefixes used in the test +prefix1 = "203.0.113.0/30" +prefix2 = "203.0.113.4/30" +prefix3 = "203.0.113.8/30" +# Next hops used for iBGP/confed routes +resolved_nh1 = "198.51.100.1" +resolved_nh2 = "198.51.100.2" +# BGP route used for recursive resolution +bgp_resolving_prefix = "198.51.100.0/24" +# Next hop that will require non-connected recursive resolution +ebgp_resolved_nh = "198.51.100.10" + + def build_topo(tgen): "Build function" @@ -125,36 +138,26 @@ def teardown_module(mod): tgen.stop_topology() -def test_bgp_peer_type_multipath_relax(): +def exabgp_cmd(peer, cmd): + pipe = open("/run/exabgp_{}.in".format(peer), "w") + with pipe: + pipe.write(cmd) + pipe.close() + + +def test_bgp_peer_type_multipath_relax_test1(): tgen = get_topogen() # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) - def exabgp_cmd(peer, cmd): - pipe = open("/run/exabgp_{}.in".format(peer), "w") - with pipe: - pipe.write(cmd) - pipe.close() - - # Prefixes used in the test - prefix1 = "203.0.113.0/30" - prefix2 = "203.0.113.4/30" - prefix3 = "203.0.113.8/30" - # Next hops used for iBGP/confed routes - resolved_nh1 = "198.51.100.1" - resolved_nh2 = "198.51.100.2" - # BGP route used for recursive resolution - bgp_resolving_prefix = "198.51.100.0/24" - # Next hop that will require non-connected recursive resolution - ebgp_resolved_nh = "198.51.100.10" + r1 = tgen.gears["r1"] # Send a non-connected route to resolve others exabgp_cmd( "peer3", "announce route {} next-hop self\n".format(bgp_resolving_prefix) ) - router = tgen.gears["r1"] # It seems that if you write to the exabgp socket too quickly in # succession, requests get lost. So verify prefix1 now instead of @@ -177,7 +180,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip bgp {} json".format(prefix1), expected, ) @@ -185,6 +188,16 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "Mixed-type multipath not found" assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test2(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + logger.info("Create and verify eBGP and iBGP+confed multipaths") exabgp_cmd( "peer1", @@ -203,38 +216,66 @@ def test_bgp_peer_type_multipath_relax(): reffile = os.path.join(CWD, "r1/multipath.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( - topotest.router_json_cmp, router, "show ip bgp json", expected + topotest.router_json_cmp, r1, "show ip bgp json", expected ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Not all expected multipaths found" assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test3(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + logger.info("Toggle peer-type multipath-relax and verify the changes") - router.vtysh_cmd( + r1.vtysh_cmd( "conf\n router bgp 64510\n no bgp bestpath peer-type multipath-relax\n" ) # This file verifies "multipath" is not set reffile = os.path.join(CWD, "r1/not-multipath.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( - topotest.router_json_cmp, router, "show ip bgp json", expected + topotest.router_json_cmp, r1, "show ip bgp json", expected ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Disabling peer-type multipath-relax did not take effect" assert res is None, assertMsg - router.vtysh_cmd( - "conf\n router bgp 64510\n bgp bestpath peer-type multipath-relax\n" - ) + +def test_bgp_peer_type_multipath_relax_test4(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + r1.vtysh_cmd("conf\n router bgp 64510\n bgp bestpath peer-type multipath-relax\n") reffile = os.path.join(CWD, "r1/multipath.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( - topotest.router_json_cmp, router, "show ip bgp json", expected + topotest.router_json_cmp, r1, "show ip bgp json", expected ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Reenabling peer-type multipath-relax did not take effect" assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test5(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + logger.info("Check recursive resolution of eBGP next hops is not affected") # eBGP next hop resolution rejects recursively resolved next hops by # default, even with peer-type multipath-relax @@ -245,7 +286,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip bgp {} json".format(prefix3), expected, ) @@ -253,6 +294,16 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3) assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test6(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + exabgp_cmd( "peer4", "announce route {} next-hop {}\n".format(prefix1, ebgp_resolved_nh) ) @@ -260,7 +311,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip bgp {} json".format(prefix1), expected, ) @@ -268,14 +319,24 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1) assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test7(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + # When other config allows recursively resolved eBGP next hops, # such next hops in all-eBGP multipaths should be valid - router.vtysh_cmd("conf\n router bgp 64510\n neighbor 10.0.4.2 ebgp-multihop\n") + r1.vtysh_cmd("conf\n router bgp 64510\n neighbor 10.0.4.2 ebgp-multihop\n") reffile = os.path.join(CWD, "r1/prefix3-recursive.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip bgp {} json".format(prefix3), expected, ) @@ -287,7 +348,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip bgp {} json".format(prefix1), expected, ) @@ -295,6 +356,16 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1) assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test8(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + logger.info("Check mixed-type multipath next hop recursive resolution in FIB") # There are now two eBGP-learned routes with a recursively resolved next; # hop; one is all-eBGP multipath, and the other is iBGP/eBGP/ @@ -305,7 +376,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip route {} json".format(prefix3), expected, ) @@ -313,6 +384,16 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "FIB next hops mismatch for all-eBGP multipath" assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test9(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + # check confed-external enables recursively resolved next hops by itself exabgp_cmd( "peer1", @@ -324,7 +405,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip route {} json".format(prefix1), expected, ) @@ -332,6 +413,16 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "FIB next hops mismatch for eBGP+confed-external multipath" assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test10(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + # check iBGP by itself exabgp_cmd( "peer1", @@ -349,7 +440,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip route {} json".format(prefix1), expected, ) diff --git a/tests/topotests/bgp_prefix_sid/exabgp.env b/tests/topotests/bgp_prefix_sid/exabgp.env index 6c554f5..bb36af5 100644 --- a/tests/topotests/bgp_prefix_sid/exabgp.env +++ b/tests/topotests/bgp_prefix_sid/exabgp.env @@ -1,5 +1,6 @@ [exabgp.api] +ack = false encoder = text highres = false respawn = false diff --git a/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg index 5b55366..a5108ff 100644 --- a/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg +++ b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg @@ -1,103 +1,101 @@ -group controller { - neighbor 10.0.0.1 { - router-id 10.0.0.101; - local-address 10.0.0.101; - local-as 2; - peer-as 1; +neighbor 10.0.0.1 { + router-id 10.0.0.101; + local-address 10.0.0.101; + local-as 2; + peer-as 1; - family { - ipv4 nlri-mpls; - } + family { + ipv4 nlri-mpls; + } - static { - # ref: draft-ietf-idr-bgp-prefix-sid-27 - # - # IANA temporarily assigned the following: - # attribute code type (suggested value: 40) to - # the BGP Prefix-SID attribute - # - # 0 1 2 3 - # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Type | Length | RESERVED | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Flags | Label Index | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Label Index | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # Figure. Label-Index TLV (Prefix-SID type-1) - # - # 0 1 2 3 - # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Type | Length | Flags | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Flags | - # +-+-+-+-+-+-+-+-+ - # - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | SRGB 1 (6 octets) | - # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | SRGB n (6 octets) | - # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<Paste> - # Figure. Originator SRGB TLV (Prefix-SID type-3) + static { + # ref: draft-ietf-idr-bgp-prefix-sid-27 + # + # IANA temporarily assigned the following: + # attribute code type (suggested value: 40) to + # the BGP Prefix-SID attribute + # + # 0 1 2 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Type | Length | RESERVED | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Flags | Label Index | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Label Index | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # Figure. Label-Index TLV (Prefix-SID type-1) + # + # 0 1 2 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Type | Length | Flags | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Flags | + # +-+-+-+-+-+-+-+-+ + # + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | SRGB 1 (6 octets) | + # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | SRGB n (6 octets) | + # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<Paste> + # Figure. Originator SRGB TLV (Prefix-SID type-3) - # ExaBGP generic-attribute binary pattern: - # Attribute-type: 0x28 (40:BGP_PREFIX_SID) - # Attribute-flag: 0xc0 (Option, Transitive) - # Attribute-body: Label-Index TLV and Originator SRGB TLV - # Label-Index TLV: 0x01000700000000000001 - # Type (08bit): 0x01 - # Length (16bit): 0x0007 - # RESERVED (08bit): 0x00 - # Flags (16bit): 0x0000 - # Label Index (32bit): 0x00000001 - # Originator SRGB TLV: 0x03000800000c350000000a - # Type (08bit): 0x03 - # Length (16bit): 0x0008 (nb-SRGB is 1) - # Flags (16bit): 0x0000 - # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) - route 3.0.0.1/32 next-hop 10.0.0.101 label [800001] attribute [0x28 0xc0 0x0100070000000000000103000800000c350000000a]; + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000001 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.1/32 next-hop 10.0.0.101 label [800001] attribute [0x28 0xc0 0x0100070000000000000103000800000c350000000a]; - # ExaBGP generic-attribute binary pattern: - # Attribute-type: 0x28 (40:BGP_PREFIX_SID) - # Attribute-flag: 0xc0 (Option, Transitive) - # Attribute-body: Label-Index TLV and Originator SRGB TLV - # Label-Index TLV: 0x01000700000000000001 - # Type (08bit): 0x01 - # Length (16bit): 0x0007 - # RESERVED (08bit): 0x00 - # Flags (16bit): 0x0000 - # Label Index (32bit): 0x00000002 - # Originator SRGB TLV: 0x03000800000c350000000a - # Type (08bit): 0x03 - # Length (16bit): 0x0008 (nb-SRGB is 1) - # Flags (16bit): 0x0000 - # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) - route 3.0.0.2/32 next-hop 10.0.0.101 label [800002] attribute [0x28 0xc0 0x0100070000000000000203000800000c350000000a]; + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000002 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.2/32 next-hop 10.0.0.101 label [800002] attribute [0x28 0xc0 0x0100070000000000000203000800000c350000000a]; - # ExaBGP generic-attribute binary pattern: - # Attribute-type: 0x28 (40:BGP_PREFIX_SID) - # Attribute-flag: 0xc0 (Option, Transitive) - # Attribute-body: Label-Index TLV and Originator SRGB TLV - # Label-Index TLV: 0x01000700000000000001 - # Type (08bit): 0x01 - # Length (16bit): 0x0007 - # RESERVED (08bit): 0x00 - # Flags (16bit): 0x0000 - # Label Index (32bit): 0x00000003 - # Originator SRGB TLV: 0x03000800000c350000000a - # Type (08bit): 0x03 - # Length (16bit): 0x0008 (nb-SRGB is 1) - # Flags (16bit): 0x0000 - # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) - route 3.0.0.3/32 next-hop 10.0.0.101 label [800003] attribute [0x28 0xc0 0x0100070000000000000303000800000c350000000a]; - } + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000003 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.3/32 next-hop 10.0.0.101 label [800003] attribute [0x28 0xc0 0x0100070000000000000303000800000c350000000a]; } } diff --git a/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg index 379d0a3..50ebdc2 100644 --- a/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg +++ b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg @@ -1,19 +1,22 @@ -group controller { - - process receive-routes { - run "/etc/exabgp/exa-receive.py --no-timestamp 2"; - receive-routes; - encoder json; - } +process receive-routes { + run /etc/exabgp/exa-receive.py --no-timestamp 2; + encoder json; +} - neighbor 10.0.0.1 { - router-id 10.0.0.102; - local-address 10.0.0.102; - local-as 3; - peer-as 1; +neighbor 10.0.0.1 { + router-id 10.0.0.102; + local-address 10.0.0.102; + local-as 3; + peer-as 1; - family { - ipv4 nlri-mpls; + family { + ipv4 nlri-mpls; + } + api { + processes [ receive-routes ]; + receive { + parsed; + update; } } } diff --git a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py index bfc083b..1e6e731 100644 --- a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py +++ b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py @@ -120,13 +120,9 @@ def exabgp_get_update_prefix(filename, afi, nexthop, prefix): ret = ret.get(afi) if ret is None: continue - ret = ret.get(nexthop) - if ret is None: - continue - ret = ret.get(prefix) - if ret is None: - continue - return output + for nh in ret.get(nexthop, []): + if nh.get("nlri") == prefix: + return output return "Not found" @@ -135,33 +131,39 @@ def test_peer2_receive_prefix_sid_type1(): peer2 = tgen.gears["peer2"] logfile = "{}/{}-received.log".format(peer2.gearlogdir, peer2.name) - def _check_type1_peer2(prefix, labelindex): + def _check_type1_peer2(prefix, label): output = exabgp_get_update_prefix( logfile, "ipv4 nlri-mpls", "10.0.0.101", prefix ) expected = { "type": "update", "neighbor": { - "ip": "10.0.0.1", + "address": { + "peer": "10.0.0.1", + }, "message": { "update": { - "attribute": { - "attribute-0x28-0xE0": "0x010007000000{:08x}".format( - labelindex - ) + "announce": { + "ipv4 nlri-mpls": { + "10.0.0.101": [ + { + "nlri": prefix, + "label": [[label]], + } + ] + } }, - "announce": {"ipv4 nlri-mpls": {"10.0.0.101": {}}}, } }, }, } return topotest.json_cmp(output, expected) - test_func = functools.partial(_check_type1_peer2, "3.0.0.1/32", labelindex=1) + test_func = functools.partial(_check_type1_peer2, "3.0.0.1/32", label=8001) success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert result is None, 'Failed _check_type1_peer2 in "{}"'.format("peer2") - test_func = functools.partial(_check_type1_peer2, "3.0.0.2/32", labelindex=2) + test_func = functools.partial(_check_type1_peer2, "3.0.0.2/32", label=8002) success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert result is None, 'Failed _check_type1_peer2 in "{}"'.format("peer2") diff --git a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg index 3819179..7a291a3 100644 --- a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg +++ b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg @@ -1,29 +1,27 @@ -group controller { - neighbor 10.0.0.1 { - router-id 10.0.0.101; - local-address 10.0.0.101; - local-as 2; - peer-as 1; +neighbor 10.0.0.1 { + router-id 10.0.0.101; + local-address 10.0.0.101; + local-as 2; + peer-as 1; - family { - ipv6 mpls-vpn; - } + family { + ipv6 mpls-vpn; + } - static { - route 2001:1::/64 { - rd 2:10; - next-hop 2001::2; - extended-community [ target:2:10 ]; - label 3; - attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; - } - route 2001:2::/64 { - rd 2:10; - next-hop 2001::2; - extended-community [ target:2:10 ]; - label 3; - attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; - } + static { + route 2001:1::/64 { + rd 2:10; + next-hop 2001::2; + extended-community [ target:2:10 ]; + label 3; + attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; + } + route 2001:2::/64 { + rd 2:10; + next-hop 2001::2; + extended-community [ target:2:10 ]; + label 3; + attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; } } } diff --git a/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf index ddc1f07..b3ca0e1 100644 --- a/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf +++ b/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf @@ -1,5 +1,4 @@ log stdout notifications -log monitor notifications !log commands ! !debug bgp zebra @@ -22,5 +21,7 @@ router bgp 1 ! address-family ipv6 vpn neighbor 10.0.0.101 activate + neighbor 10.0.0.101 route-map DENY_ALL out exit-address-family ! +route-map DENY_ALL deny 10 diff --git a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json index 42293b1..65c51f1 100644 --- a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json +++ b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json @@ -1,10 +1,6 @@ { "2:10":{ "prefix":"2001:1::\/64", - "advertisedTo":{ - "10.0.0.101":{ - } - }, "paths":[ { "aspath":{ diff --git a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json index c9ad871..4a12c2a 100644 --- a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json +++ b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json @@ -1,10 +1,6 @@ { "2:10":{ "prefix":"2001:2::\/64", - "advertisedTo":{ - "10.0.0.101":{ - } - }, "paths":[ { "aspath":{ diff --git a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py index 35459f6..88251fa 100644 --- a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py +++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py @@ -257,10 +257,10 @@ def test_recursive_routes_iBGP_peer_p1(request): ) dut = "r2" create_interface_in_kernel( - tgen, dut, "lo", "40.40.40.50", netmask="255.255.255.0", create=True + tgen, dut, "lo10", "40.40.40.50", netmask="255.255.255.0", create=True ) create_interface_in_kernel( - tgen, dut, "lo", "40:40::40:50", netmask="120", create=True + tgen, dut, "lo10", "40:40::40:50", netmask="120", create=True ) for addr_type in ADDR_TYPES: input_dict_3 = { diff --git a/tests/topotests/bgp_redistribute_table/__init__.py b/tests/topotests/bgp_redistribute_table/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/__init__.py diff --git a/tests/topotests/bgp_redistribute_table/r1/bgpd.conf b/tests/topotests/bgp_redistribute_table/r1/bgpd.conf new file mode 100644 index 0000000..c5e0fcd --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r1/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as 65501 + address-family ipv4 unicast + neighbor 192.168.0.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_all_redistribute.json b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_all_redistribute.json new file mode 100644 index 0000000..e5a27f3 --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_all_redistribute.json @@ -0,0 +1,71 @@ +{ + "172.31.0.2/32": [ + { + "prefix": "172.31.0.2/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "172.31.0.10/32": [ + { + "prefix": "172.31.0.10/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "172.31.0.15/32": [ + { + "prefix": "172.31.0.15/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_redistribute.json b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_redistribute.json new file mode 100644 index 0000000..1304edd --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_redistribute.json @@ -0,0 +1,48 @@ +{ + "172.31.0.2/32": [ + { + "prefix": "172.31.0.2/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "172.31.0.10/32": [ + { + "prefix": "172.31.0.10/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_without_redistribute.json b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_without_redistribute.json new file mode 100644 index 0000000..74f594f --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_without_redistribute.json @@ -0,0 +1,25 @@ +{ + "172.31.0.2/32": [ + { + "prefix": "172.31.0.2/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_redistribute_table/r1/zebra.conf b/tests/topotests/bgp_redistribute_table/r1/zebra.conf new file mode 100644 index 0000000..abe6d39 --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r1/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface r1-eth1 + ip address 172.31.0.1/32 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bgp_redistribute_table/r2/bgpd.conf b/tests/topotests/bgp_redistribute_table/r2/bgpd.conf new file mode 100644 index 0000000..2ce7043 --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r2/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65501 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as 65500 + address-family ipv4 unicast + network 172.31.0.2/32 + neighbor 192.168.0.1 activate + redistribute table-direct 2200 + exit-address-family +! diff --git a/tests/topotests/bgp_redistribute_table/r2/zebra.conf b/tests/topotests/bgp_redistribute_table/r2/zebra.conf new file mode 100644 index 0000000..89ad2ec --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r2/zebra.conf @@ -0,0 +1,8 @@ +log stdout +interface r2-eth0 + ip address 192.168.0.2/24 +! +interface r2-eth1 + ip address 172.31.0.2/32 + ip address 172.31.1.2/24 +! diff --git a/tests/topotests/bgp_redistribute_table/test_bgp_redistribute_table.py b/tests/topotests/bgp_redistribute_table/test_bgp_redistribute_table.py new file mode 100644 index 0000000..08b70ae --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/test_bgp_redistribute_table.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_redistribute_table.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by 6WIND +# + +""" + test_bgp_redistribute_table.py: Test the FRR BGP daemon with 'redistribute table-direct' +""" + +import os +import sys +import json +from functools import partial +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.common_config import step +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def _router_json_cmp_exact_filter(router, cmd, expected): + output = router.vtysh_cmd(cmd) + logger.info("{}: {}\n{}".format(router.name, cmd, output)) + + json_output = json.loads(output) + + # filter out tableVersion, version, nhVrfId and vrfId + for route, attrs in json_output.items(): + for attr in attrs: + if "table" in attr: + attr.pop("table") + if "internalStatus" in attr: + attr.pop("internalStatus") + if "internalFlags" in attr: + attr.pop("internalFlags") + if "internalNextHopNum" in attr: + attr.pop("internalNextHopNum") + if "internalNextHopActiveNum" in attr: + attr.pop("internalNextHopActiveNum") + if "nexthopGroupId" in attr: + attr.pop("nexthopGroupId") + if "installedNexthopGroupId" in attr: + attr.pop("installedNexthopGroupId") + if "uptime" in attr: + attr.pop("uptime") + if "prefixLen" in attr: + attr.pop("prefixLen") + if "asPath" in attr: + attr.pop("asPath") + for nexthop in attr.get("nexthops", []): + if "flags" in nexthop: + nexthop.pop("flags") + if "interfaceIndex" in nexthop: + nexthop.pop("interfaceIndex") + + return topotest.json_cmp(json_output, expected, exact=True) + + +def _check_zebra_rib_r1(with_redistributed_route, with_second_route=False): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + with_str = "" if with_redistributed_route else "out" + + router = tgen.gears["r1"] + if with_redistributed_route: + with_str = "" + if with_second_route: + json_file = "{}/{}/ipv4_routes_with_all_redistribute.json".format( + CWD, router.name + ) + else: + json_file = "{}/{}/ipv4_routes_with_redistribute.json".format( + CWD, router.name + ) + else: + with_str = "out" + json_file = "{}/{}/ipv4_routes_without_redistribute.json".format( + CWD, router.name + ) + + step(f"Checking IPv4 routes for convergence on r1 with{with_str} kernel route") + expected = json.loads(open(json_file).read()) + test_func = partial( + _router_json_cmp_exact_filter, + router, + "show ip route bgp json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def _test_add_and_check_kernel_route_on_table_2200(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_list = tgen.routers() + + step("r2, adding new kernel route 172.31.0.10/32 on table 2200") + cmd = "ip route add 172.31.0.10/32 via 172.31.1.10 table 2200" + tgen.net["r2"].cmd(cmd) + + _check_zebra_rib_r1(True) + + +def test_step1_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking IPv4 routes for convergence on r1") + _check_zebra_rib_r1(False) + + +def test_step2_add_kernel_route_on_table_2200(): + """ + On r2, create a kernel route on table 2200 + * Check that the kernel route is redistributed to r1 + """ + _test_add_and_check_kernel_route_on_table_2200() + + +def test_step3_remove_kernel_route_on_table_2200(): + """ + On r2, remove a kernel route on table 2200 + * Check that the kernel route is no more redistributed to r1 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_list = tgen.routers() + + step("r2, remove a kernel route on table 2200") + cmd = "ip route delete 172.31.0.10/32 via 172.31.1.10 table 2200" + tgen.net["r2"].cmd(cmd) + + _check_zebra_rib_r1(False) + + +def test_step4_add_kernel_route_on_table_2200(): + """ + On r2, add a kernel route on table 2200 + * Check that the kernel route is redistributed to r1 + """ + _test_add_and_check_kernel_route_on_table_2200() + + +def test_step5_no_redistribute_table_2200(): + """ + On r2, unconfigure the 'no redistribute' service + * Check that the 'redistribute' command is not configured + * Check that the kernel route is not redistributed to r1 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r2"].vtysh_cmd( + "configure terminal\nrouter bgp 65501\naddress-family ipv4 unicast\nno redistribute table-direct\n" + ) + + step("r2, check that the 'redistribute' command is not configured") + out = tgen.net["r2"].cmd( + "vtysh -c 'show running-config' | grep 'redistribute table-direct'" + ) + + if "redistribute" in out: + assert True, "r2, redistribute command still present" + + _check_zebra_rib_r1(False) + + +def test_step6_redistribute_table_2200(): + """ + On r2, configure the 'redistribute' service + * Check that the 'redistribute' command is configured + * Check that the kernel route is redistributed to r1 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r2"].vtysh_cmd( + "configure terminal\nrouter bgp 65501\naddress-family ipv4 unicast\nredistribute table-direct 2200\n" + ) + + step("r2, check that the 'redistribute' command is configured") + out = tgen.net["r2"].cmd( + "vtysh -c 'show running-config' | grep 'redistribute table-direct'" + ) + if "redistribute" not in out: + assert True, "r2, redistribute command still present" + + _check_zebra_rib_r1(True) + + +def test_step7_reset_bgp_instance_add_kernel_route_and_add_bgp(): + """ + On r2, remove BGP configuration, create a kernel route on table 2200, + then restore BGP configuration + * Check that the kernel route is redistributed to r1 + """ + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_list = tgen.routers() + + router = tgen.gears["r2"] + step("r2, removing r2 BGP configuration") + router.vtysh_cmd("configure terminal\nno router bgp 65501\n") + + step("r2, adding new kernel route 172.31.0.15/32 on table 2200") + cmd = "ip route add 172.31.0.15/32 via 172.31.1.100 table 2200" + tgen.net["r2"].cmd(cmd) + + router = tgen.gears["r2"] + step("r2, restoring r2 BGP configuration") + tgen.net["r2"].cmd("vtysh -f {}".format(os.path.join(CWD, "r2/bgpd.conf"))) + + _check_zebra_rib_r1(True, with_second_route=True) + + +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_route_server_client/r1/bgpd.conf b/tests/topotests/bgp_route_server_client/r1/bgpd.conf index 9826b67..e464e6c 100644 --- a/tests/topotests/bgp_route_server_client/r1/bgpd.conf +++ b/tests/topotests/bgp_route_server_client/r1/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65001 bgp router-id 10.10.10.1 no bgp ebgp-requires-policy + no bgp enforce-first-as neighbor 2001:db8:1::1 remote-as external neighbor 2001:db8:1::1 timers 3 10 neighbor 2001:db8:1::1 timers connect 5 diff --git a/tests/topotests/bgp_rpki_topo1/__init__.py b/tests/topotests/bgp_rpki_topo1/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/__init__.py diff --git a/tests/topotests/bgp_rpki_topo1/r1/bgpd.conf b/tests/topotests/bgp_rpki_topo1/r1/bgpd.conf new file mode 100644 index 0000000..437d393 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r1/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 65530 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.0.2.2 remote-as 65002 + neighbor 192.0.2.2 timers 1 3 + neighbor 192.0.2.2 timers connect 1 + neighbor 192.0.2.2 ebgp-multihop 3 + neighbor 192.0.2.2 update-source 192.0.2.1 + address-family ipv4 unicast + network 198.51.100.0/24 + network 203.0.113.0/24 + network 10.0.0.0/24 + exit-address-family +! diff --git a/tests/topotests/bgp_rpki_topo1/r1/rtrd.py b/tests/topotests/bgp_rpki_topo1/r1/rtrd.py new file mode 100755 index 0000000..bca58a6 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r1/rtrd.py @@ -0,0 +1,330 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0-or-later + +# Copyright (C) 2023 Tomas Hlavacek (tmshlvck@gmail.com) + +from typing import List, Tuple, Callable, Type +import socket +import threading +import socketserver +import struct +import ipaddress +import csv +import os +import sys + +LISTEN_HOST, LISTEN_PORT = "0.0.0.0", 15432 +VRPS_FILE = os.path.join(sys.path[0], "vrps.csv") + + +def dbg(m: str): + print(m) + sys.stdout.flush() + + +class RTRDatabase(object): + def __init__(self, vrps_file: str) -> None: + self.last_serial = 0 + self.ann4 = [] + self.ann6 = [] + self.withdraw4 = [] + self.withdraw6 = [] + + with open(vrps_file, "r") as fh: + for rasn, rnet, rmaxlen, _ in csv.reader(fh): + try: + net = ipaddress.ip_network(rnet) + asn = int(rasn[2:]) + maxlen = int(rmaxlen) + if net.version == 6: + self.ann6.append((asn, str(net), maxlen)) + elif net.version == 4: + self.ann4.append((asn, str(net), maxlen)) + else: + raise ValueError(f"Unknown AFI: {net.version}") + except Exception as e: + dbg( + f"VRPS load: ignoring {str((rasn, rnet,rmaxlen))} because {str(e)}" + ) + + def get_serial(self) -> int: + return self.last_serial + + def set_serial(self, serial: int) -> None: + self.last_serial = serial + + def get_announcements4(self, serial: int = 0) -> List[Tuple[int, str, int]]: + if serial > self.last_serial: + return self.ann4 + else: + return [] + + def get_withdrawals4(self, serial: int = 0) -> List[Tuple[int, str, int]]: + if serial > self.last_serial: + return self.withdraw4 + else: + return [] + + def get_announcements6(self, serial: int = 0) -> List[Tuple[int, str, int]]: + if serial > self.last_serial: + return self.ann6 + else: + return [] + + def get_withdrawals6(self, serial: int = 0) -> List[Tuple[int, str, int]]: + if serial > self.last_serial: + return self.withdraw6 + else: + return [] + + +class RTRConnHandler(socketserver.BaseRequestHandler): + PROTO_VERSION = 0 + + def setup(self) -> None: + self.session_id = 2345 + self.serial = 1024 + + dbg(f"New connection from: {str(self.client_address)} ") + # TODO: register for notifies + + def finish(self) -> None: + pass + # TODO: de-register + + HEADER_LEN = 8 + + def decode_header(self, buf: bytes) -> Tuple[int, int, int, int]: + # common header in all received packets + return struct.unpack("!BBHI", buf) + # reutnrs (proto_ver, pdu_type, sess_id, length) + + SERNOTIFY_TYPE = 0 + SERNOTIFY_LEN = 12 + + def send_sernotify(self, serial: int) -> None: + # serial notify PDU + dbg(f"<Serial Notify session_id={self.session_id} serial={serial}") + self.request.send( + struct.pack( + "!BBHII", + self.PROTO_VERSION, + self.SERNOTIFY_TYPE, + self.session_id, + self.SERNOTIFY_LEN, + serial, + ) + ) + + CACHERESPONSE_TYPE = 3 + CACHERESPONSE_LEN = 8 + + def send_cacheresponse(self) -> None: + # cache response PDU + dbg(f"<Cache response session_id={self.session_id}") + self.request.send( + struct.pack( + "!BBHI", + self.PROTO_VERSION, + self.CACHERESPONSE_TYPE, + self.session_id, + self.CACHERESPONSE_LEN, + ) + ) + + FLAGS_ANNOUNCE = 1 + FLAGS_WITHDRAW = 0 + + IPV4_TYPE = 4 + IPV4_LEN = 20 + + def send_ipv4(self, ipnet: str, asn: int, maxlen: int, flags: int): + # IPv4 PDU + dbg(f"<IPv4 net={ipnet} asn={asn} maxlen={maxlen} flags={flags}") + ip = ipaddress.IPv4Network(ipnet) + self.request.send( + struct.pack( + "!BBHIBBBB4sI", + self.PROTO_VERSION, + self.IPV4_TYPE, + 0, + self.IPV4_LEN, + flags, + ip.prefixlen, + maxlen, + 0, + ip.network_address.packed, + asn, + ) + ) + + def announce_ipv4(self, ipnet, asn, maxlen): + self.send_ipv4(ipnet, asn, maxlen, self.FLAGS_ANNOUNCE) + + def withdraw_ipv4(self, ipnet, asn, maxlen): + self.send_ipv4(ipnet, asn, maxlen, self.FLAGS_WITHDRAW) + + IPV6_TYPE = 6 + IPV6_LEN = 32 + + def send_ipv6(self, ipnet: str, asn: int, maxlen: int, flags: int): + # IPv6 PDU + dbg(f"<IPv6 net={ipnet} asn={asn} maxlen={maxlen} flags={flags}") + ip = ipaddress.IPv6Network(ipnet) + self.request.send( + struct.pack( + "!BBHIBBBB16sI", + self.PROTO_VERSION, + self.IPV6_TYPE, + 0, + self.IPV6_LEN, + flags, + ip.prefixlen, + maxlen, + 0, + ip.network_address.packed, + asn, + ) + ) + + def announce_ipv6(self, ipnet: str, asn: int, maxlen: int): + self.send_ipv6(ipnet, asn, maxlen, self.FLAGS_ANNOUNCE) + + def withdraw_ipv6(self, ipnet: str, asn: int, maxlen: int): + self.send_ipv6(ipnet, asn, maxlen, self.FLAGS_WITHDRAW) + + EOD_TYPE = 7 + EOD_LEN = 12 + + def send_endofdata(self, serial: int): + # end of data PDU + dbg(f"<End of Data session_id={self.session_id} serial={serial}") + self.server.db.set_serial(serial) + self.request.send( + struct.pack( + "!BBHII", + self.PROTO_VERSION, + self.EOD_TYPE, + self.session_id, + self.EOD_LEN, + serial, + ) + ) + + CACHERESET_TYPE = 8 + CACHERESET_LEN = 8 + + def send_cachereset(self): + # cache reset PDU + dbg("<Cache Reset") + self.request.send( + struct.pack( + "!BBHI", + self.PROTO_VERSION, + self.CACHERESET_TYPE, + 0, + self.CACHERESET_LEN, + ) + ) + + SERIAL_QUERY_TYPE = 1 + SERIAL_QUERY_LEN = 12 + + def handle_serial_query(self, buf: bytes, sess_id: int): + serial = struct.unpack("!I", buf)[0] + dbg(f">Serial query: {serial}") + if sess_id: + self.server.db.set_serial(serial) + else: + self.server.db.set_serial(0) + self.send_cacheresponse() + + for asn, ipnet, maxlen in self.server.db.get_announcements4(serial): + self.announce_ipv4(ipnet, asn, maxlen) + + for asn, ipnet, maxlen in self.server.db.get_withdrawals4(serial): + self.withdraw_ipv4(ipnet, asn, maxlen) + + for asn, ipnet, maxlen in self.server.db.get_announcements6(serial): + self.announce_ipv6(ipnet, asn, maxlen) + + for asn, ipnet, maxlen in self.server.db.get_withdrawals6(serial): + self.withdraw_ipv6(ipnet, asn, maxlen) + + self.send_endofdata(self.serial) + + RESET_TYPE = 2 + + def handle_reset(self): + dbg(">Reset") + self.session_id += 1 + self.server.db.set_serial(0) + self.send_cacheresponse() + + for asn, ipnet, maxlen in self.server.db.get_announcements4(self.serial): + self.announce_ipv4(ipnet, asn, maxlen) + + for asn, ipnet, maxlen in self.server.db.get_announcements6(self.serial): + self.announce_ipv6(ipnet, asn, maxlen) + + self.send_endofdata(self.serial) + + ERROR_TYPE = 10 + + def handle_error(self, buf: bytes): + dbg(f">Error: {str(buf)}") + self.server.shutdown() + self.server.stopped = True + raise ConnectionError("Received an RPKI error packet from FRR. Exiting") + + def handle(self): + while True: + b = self.request.recv(self.HEADER_LEN, socket.MSG_WAITALL) + if len(b) == 0: + break + proto_ver, pdu_type, sess_id, length = self.decode_header(b) + dbg( + f">Header proto_ver={proto_ver} pdu_type={pdu_type} sess_id={sess_id} length={length}" + ) + + if sess_id: + self.session_id = sess_id + + if pdu_type == self.SERIAL_QUERY_TYPE: + b = self.request.recv( + self.SERIAL_QUERY_LEN - self.HEADER_LEN, socket.MSG_WAITALL + ) + self.handle_serial_query(b, sess_id) + + elif pdu_type == self.RESET_TYPE: + self.handle_reset() + + elif pdu_type == self.ERROR_TYPE: + b = self.request.recv(length - self.HEADER_LEN, socket.MSG_WAITALL) + self.handle_error(b) + + +class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): + def __init__( + self, bind: Tuple[str, int], handler: Type[RTRConnHandler], db: RTRDatabase + ) -> None: + super().__init__(bind, handler) + self.db = db + + +def main(): + db = RTRDatabase(VRPS_FILE) + server = ThreadedTCPServer((LISTEN_HOST, LISTEN_PORT), RTRConnHandler, db) + dbg(f"Server listening on {LISTEN_HOST} port {LISTEN_PORT}") + server.serve_forever() + + +if __name__ == "__main__": + if len(sys.argv) > 1: + f = open(sys.argv[1], "w") + sys.__stdout__ = f + sys.stdout = f + sys.__stderr__ = f + sys.stderr = f + + main() diff --git a/tests/topotests/bgp_rpki_topo1/r1/staticd.conf b/tests/topotests/bgp_rpki_topo1/r1/staticd.conf new file mode 100644 index 0000000..7f2f057 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r1/staticd.conf @@ -0,0 +1 @@ +ip route 192.0.2.2/32 192.168.1.2 diff --git a/tests/topotests/bgp_rpki_topo1/r1/vrps.csv b/tests/topotests/bgp_rpki_topo1/r1/vrps.csv new file mode 100644 index 0000000..5a6e023 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r1/vrps.csv @@ -0,0 +1,3 @@ +ASN,IP Prefix,Max Length,Trust Anchor +AS65530,198.51.100.0/24,24,private +AS65530,203.0.113.0/24,24,private diff --git a/tests/topotests/bgp_rpki_topo1/r1/zebra.conf b/tests/topotests/bgp_rpki_topo1/r1/zebra.conf new file mode 100644 index 0000000..b742b70 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r1/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 192.0.2.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_any.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_any.json new file mode 100644 index 0000000..a04e9ef --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_any.json @@ -0,0 +1,37 @@ +{ + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65002, + "routes": { + "198.51.100.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "198.51.100.0", + "prefixLen": 24, + "network": "198.51.100.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ], + "203.0.113.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "203.0.113.0", + "prefixLen": 24, + "network": "203.0.113.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ] + } +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_notfound.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_notfound.json new file mode 100644 index 0000000..01e288c --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_notfound.json @@ -0,0 +1,7 @@ +{ + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65002, + "routes": { + } +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_valid.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_valid.json new file mode 120000 index 0000000..2645bfa --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_valid.json @@ -0,0 +1 @@ +bgp_table_rpki_valid.json
\ No newline at end of file diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_any.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_any.json new file mode 100644 index 0000000..5546d45 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_any.json @@ -0,0 +1,52 @@ +{ + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65002, + "routes": { + "10.0.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.0.0.0", + "prefixLen": 24, + "network": "10.0.0.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ], + "198.51.100.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "198.51.100.0", + "prefixLen": 24, + "network": "198.51.100.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ], + "203.0.113.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "203.0.113.0", + "prefixLen": 24, + "network": "203.0.113.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ] + } +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_notfound.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_notfound.json new file mode 100644 index 0000000..7b9a5c8 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_notfound.json @@ -0,0 +1,22 @@ +{ + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65002, + "routes": { + "10.0.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.0.0.0", + "prefixLen": 24, + "network": "10.0.0.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ] + } +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_valid.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_valid.json new file mode 100644 index 0000000..eb3852a --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_valid.json @@ -0,0 +1,35 @@ +{ + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65002, + "routes": { + "198.51.100.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "198.51.100.0", + "prefixLen": 24, + "network": "198.51.100.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ], + "203.0.113.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "203.0.113.0", + "prefixLen": 24, + "network": "203.0.113.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ] + } +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgpd.conf b/tests/topotests/bgp_rpki_topo1/r2/bgpd.conf new file mode 100644 index 0000000..87d7214 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.0.2.1 remote-as 65530 + neighbor 192.0.2.1 timers connect 1 + neighbor 192.0.2.1 ebgp-multihop 3 + neighbor 192.0.2.1 update-source 192.0.2.2 + neighbor 192.168.4.4 remote-as internal + neighbor 192.168.4.4 timers 1 3 + neighbor 192.168.4.4 timers connect 1 + address-family ipv4 unicast + neighbor 192.168.4.4 next-hop-self + exit-address-family +! +router bgp 65002 vrf vrf10 + no bgp ebgp-requires-policy + neighbor 192.0.2.3 remote-as 65530 + neighbor 192.0.2.3 timers 1 3 + neighbor 192.0.2.3 timers connect 1 + neighbor 192.0.2.3 ebgp-multihop 3 + neighbor 192.0.2.3 update-source 192.0.2.2 +! +rpki + rpki retry_interval 5 + rpki cache 192.0.2.1 15432 preference 1 +exit diff --git a/tests/topotests/bgp_rpki_topo1/r2/rpki_prefix_table.json b/tests/topotests/bgp_rpki_topo1/r2/rpki_prefix_table.json new file mode 100644 index 0000000..fbc5cc9 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/rpki_prefix_table.json @@ -0,0 +1,18 @@ +{ + "prefixes":[ + { + "prefix":"198.51.100.0", + "prefixLenMin":24, + "prefixLenMax":24, + "asn":65530 + }, + { + "prefix":"203.0.113.0", + "prefixLenMin":24, + "prefixLenMax":24, + "asn":65530 + } + ], + "ipv4PrefixCount":2, + "ipv6PrefixCount":0 +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/staticd.conf b/tests/topotests/bgp_rpki_topo1/r2/staticd.conf new file mode 100644 index 0000000..de58dde --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/staticd.conf @@ -0,0 +1,5 @@ +ip route 192.0.2.1/32 192.168.1.1 +! +vrf vrf10 + ip route 192.0.2.3/32 192.168.2.3 +! diff --git a/tests/topotests/bgp_rpki_topo1/r2/zebra.conf b/tests/topotests/bgp_rpki_topo1/r2/zebra.conf new file mode 100644 index 0000000..785dbc6 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/zebra.conf @@ -0,0 +1,15 @@ +interface lo + ip address 192.0.2.2/32 +! +interface vrf10 vrf vrf10 + ip address 192.0.2.2/32 +! +interface r2-eth0 + ip address 192.168.1.2/24 +! +interface r2-eth1 vrf vrf10 + ip address 192.168.2.2/24 +! +interface r2-eth2 + ip address 192.168.4.2/24 +! diff --git a/tests/topotests/bgp_rpki_topo1/r3/bgpd.conf b/tests/topotests/bgp_rpki_topo1/r3/bgpd.conf new file mode 100644 index 0000000..596dc20 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 65530 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.0.2.2 remote-as 65002 + neighbor 192.0.2.2 timers 1 3 + neighbor 192.0.2.2 timers connect 1 + neighbor 192.0.2.2 ebgp-multihop 3 + neighbor 192.0.2.2 update-source 192.0.2.3 + address-family ipv4 unicast + network 198.51.100.0/24 + network 203.0.113.0/24 + network 10.0.0.0/24 + exit-address-family +! diff --git a/tests/topotests/bgp_rpki_topo1/r3/rtrd.py b/tests/topotests/bgp_rpki_topo1/r3/rtrd.py new file mode 120000 index 0000000..1c5871a --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/rtrd.py @@ -0,0 +1 @@ +../r1/rtrd.py
\ No newline at end of file diff --git a/tests/topotests/bgp_rpki_topo1/r3/staticd.conf b/tests/topotests/bgp_rpki_topo1/r3/staticd.conf new file mode 100644 index 0000000..6822f7e --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/staticd.conf @@ -0,0 +1 @@ +ip route 192.0.2.2/32 192.168.2.2 diff --git a/tests/topotests/bgp_rpki_topo1/r3/vrps.csv b/tests/topotests/bgp_rpki_topo1/r3/vrps.csv new file mode 120000 index 0000000..8daa27f --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/vrps.csv @@ -0,0 +1 @@ +../r1/vrps.csv
\ No newline at end of file diff --git a/tests/topotests/bgp_rpki_topo1/r3/zebra.conf b/tests/topotests/bgp_rpki_topo1/r3/zebra.conf new file mode 100644 index 0000000..0975114 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/zebra.conf @@ -0,0 +1,5 @@ +interface lo + ip address 192.0.2.3/32 +! +interface r3-eth0 + ip address 192.168.2.3/24
\ No newline at end of file diff --git a/tests/topotests/bgp_rpki_topo1/r4/bgpd.conf b/tests/topotests/bgp_rpki_topo1/r4/bgpd.conf new file mode 100644 index 0000000..80dc9ca --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r4/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.4.2 remote-as internal + neighbor 192.168.4.2 timers 1 3 + neighbor 192.168.4.2 timers connect 1 +! diff --git a/tests/topotests/bgp_rpki_topo1/r4/zebra.conf b/tests/topotests/bgp_rpki_topo1/r4/zebra.conf new file mode 100644 index 0000000..ed793ae --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r4/zebra.conf @@ -0,0 +1,4 @@ +! +interface r4-eth0 + ip address 192.168.4.4/24 +! diff --git a/tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py b/tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py new file mode 100644 index 0000000..a12204f --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py @@ -0,0 +1,453 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright 2023 6WIND S.A. + +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 +from lib.topolog import logger + +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 = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, 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_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, "{}/bgpd.conf".format(rname)), + " -M bgpd_rpki" if rname == "r2" else "", + ) + + 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 set r2-eth1 master vrf10") + + tgen.start_router() + + global rtrd_process + rtrd_process = {} + + for rname in ["r1", "r3"]: + rtr_path = os.path.join(CWD, rname) + log_dir = os.path.join(tgen.logdir, rname) + log_file = os.path.join(log_dir, "rtrd.log") + + tgen.gears[rname].cmd("chmod u+x {}/rtrd.py".format(rtr_path)) + rtrd_process[rname] = tgen.gears[rname].popen( + "{}/rtrd.py {}".format(rtr_path, log_file) + ) + + +def teardown_module(mod): + tgen = get_topogen() + + for rname in ["r1", "r3"]: + logger.info("{}: sending SIGTERM to rtrd RPKI server".format(rname)) + rtrd_process[rname].kill() + + tgen.stop_topology() + + +def show_rpki_prefixes(rname, expected, vrf=None): + tgen = get_topogen() + + if vrf: + cmd = "show rpki prefix-table vrf {} json".format(vrf) + else: + cmd = "show rpki prefix-table json" + + output = json.loads(tgen.gears[rname].vtysh_cmd(cmd)) + + return topotest.json_cmp(output, expected) + + +def show_bgp_ipv4_table_rpki(rname, rpki_state, expected, vrf=None): + tgen = get_topogen() + + cmd = "show bgp" + if vrf: + cmd += " vrf {}".format(vrf) + cmd += " ipv4 unicast" + if rpki_state: + cmd += " rpki {}".format(rpki_state) + cmd += " json" + + output = json.loads(tgen.gears[rname].vtysh_cmd(cmd)) + + expected_nb = len(expected.get("routes")) + output_nb = len(output.get("routes", {})) + + if expected_nb != output_nb: + return {"error": "expected {} prefixes. Got {}".format(expected_nb, output_nb)} + + return topotest.json_cmp(output, expected) + + +def test_show_bgp_rpki_prefixes(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + rname = "r2" + + step("Check RPKI prefix table") + + expected = open(os.path.join(CWD, "{}/rpki_prefix_table.json".format(rname))).read() + expected_json = json.loads(expected) + test_func = functools.partial(show_rpki_prefixes, rname, expected_json) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see RPKI prefixes on {}".format(rname) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step("Check RPKI state of prefixes in BGP table: {}".format(rpki_state)) + else: + step("Check prefixes in BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + test_func = functools.partial( + show_bgp_ipv4_table_rpki, rname, rpki_state, expected_json + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + +def test_show_bgp_rpki_prefixes_no_rpki_cache(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + def _show_rpki_no_connection(rname): + output = json.loads( + tgen.gears[rname].vtysh_cmd("show rpki cache-connection json") + ) + + return output == {"error": "No connection to RPKI cache server."} + + step("Remove RPKI server from configuration") + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +rpki + no rpki cache 192.0.2.1 15432 preference 1 +exit +""" + ) + + step("Check RPKI connection state") + + test_func = functools.partial(_show_rpki_no_connection, rname) + _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5) + assert result, "RPKI is still connected on {}".format(rname) + + +def test_show_bgp_rpki_prefixes_reconnect(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + step("Restore RPKI server configuration") + + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +rpki + rpki cache 192.0.2.1 15432 preference 1 +exit +""" + ) + + step("Check RPKI prefix table") + + expected = open(os.path.join(CWD, "{}/rpki_prefix_table.json".format(rname))).read() + expected_json = json.loads(expected) + test_func = functools.partial(show_rpki_prefixes, rname, expected_json) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see RPKI prefixes on {}".format(rname) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step("Check RPKI state of prefixes in BGP table: {}".format(rpki_state)) + else: + step("Check prefixes in BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + +def test_show_bgp_rpki_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + step("Apply RPKI valid route-map on neighbor") + + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +route-map RPKI permit 10 + match rpki valid +! +router bgp 65002 + address-family ipv4 unicast + neighbor 192.0.2.1 route-map RPKI in +""" + ) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step("Check RPKI state of prefixes in BGP table: {}".format(rpki_state)) + else: + step("Check prefixes in BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rmap_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + test_func = functools.partial( + show_bgp_ipv4_table_rpki, + rname, + rpki_state, + expected_json, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + +def test_show_bgp_rpki_prefixes_vrf(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + step("Configure RPKI cache server on vrf10") + + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +vrf vrf10 + rpki + rpki cache 192.0.2.3 15432 preference 1 + exit +exit +""" + ) + + step("Check vrf10 RPKI prefix table") + + expected = open(os.path.join(CWD, "{}/rpki_prefix_table.json".format(rname))).read() + expected_json = json.loads(expected) + test_func = functools.partial(show_rpki_prefixes, rname, expected_json, vrf="vrf10") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see RPKI prefixes on {}".format(rname) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step( + "Check RPKI state of prefixes in vrf10 BGP table: {}".format(rpki_state) + ) + else: + step("Check prefixes in vrf10 BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + test_func = functools.partial( + show_bgp_ipv4_table_rpki, rname, rpki_state, expected_json, vrf="vrf10" + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + +def test_show_bgp_rpki_route_map_vrf(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + step("Apply RPKI valid route-map on vrf10 neighbor") + + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +router bgp 65002 vrf vrf10 + address-family ipv4 unicast + neighbor 192.0.2.3 route-map RPKI in +""" + ) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step( + "Check RPKI state of prefixes in vrf10 BGP table: {}".format(rpki_state) + ) + else: + step("Check prefixes in vrf10 BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rmap_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + test_func = functools.partial( + show_bgp_ipv4_table_rpki, + rname, + rpki_state, + expected_json, + vrf="vrf10", + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + +def test_bgp_ecommunity_rpki(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r4 = tgen.gears["r4"] + + # Flush all the states what was before and try sending out the prefixes + # with RPKI extended community. + r2.vtysh_cmd("clear ip bgp 192.168.4.4 soft out") + + def _bgp_check_ecommunity_rpki(community=None): + output = json.loads(r4.vtysh_cmd("show bgp ipv4 unicast 198.51.100.0/24 json")) + expected = { + "paths": [ + { + "extendedCommunity": community, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_ecommunity_rpki, {"string": "OVS:valid"}) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Didn't receive RPKI extended community" + + r2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + address-family ipv4 unicast + no neighbor 192.168.4.4 send-community extended rpki + """ + ) + + test_func = functools.partial(_bgp_check_ecommunity_rpki) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Received RPKI extended community" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py index a0cd89f..d373a74 100644 --- a/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py +++ b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py @@ -62,23 +62,48 @@ def teardown_module(mod): tgen.stop_topology() +expected_1 = { + "routes": { + "172.16.255.31/32": [{"path": "65002"}], + "172.16.255.32/32": [{"path": ""}], + } +} + +expected_2 = { + "routes": { + "172.16.255.31/32": [{"path": ""}], + "172.16.255.32/32": [{"path": ""}], + } +} + +expected_3 = { + "routes": { + "172.16.255.31/32": [{"path": "65003"}], + "172.16.255.32/32": [{"path": "65003"}], + } +} + +expected_4 = { + "routes": { + "172.16.255.31/32": [{"path": "65002 65003"}], + "172.16.255.32/32": [{"path": "65002 65003"}], + } +} + + +def bgp_converge(router, expected): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + + return topotest.json_cmp(output, expected) + + def test_bgp_set_aspath_exclude(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - def _bgp_converge(router): - output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) - expected = { - "routes": { - "172.16.255.31/32": [{"path": "65002"}], - "172.16.255.32/32": [{"path": ""}], - } - } - return topotest.json_cmp(output, expected) - - test_func = functools.partial(_bgp_converge, tgen.gears["r1"]) + test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_1) _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) assert result is None, "Failed overriding incoming AS-PATH with route-map" @@ -102,19 +127,7 @@ conf """ ) - expected = { - "routes": { - "172.16.255.31/32": [{"path": ""}], - "172.16.255.32/32": [{"path": ""}], - } - } - - def _bgp_regexp_1(router): - output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) - - return topotest.json_cmp(output, expected) - - test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"]) + test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_2) _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) assert result is None, "Failed overriding incoming AS-PATH with regex 1 route-map" @@ -127,19 +140,46 @@ conf """ ) - expected = { - "routes": { - "172.16.255.31/32": [{"path": "65003"}], - "172.16.255.32/32": [{"path": "65003"}], - } - } - - test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"]) + # tgen.mininet_cli() + test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_3) _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) assert result is None, "Failed overriding incoming AS-PATH with regex 2 route-map" +def test_no_bgp_set_aspath_exclude_access_list(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + rname = "r1" + r1 = tgen.gears[rname] + + r1.vtysh_cmd( + """ +conf + no bgp as-path access-list SECOND permit 2 + """ + ) + + test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_3) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed removing bgp as-path access-list" + + r1.vtysh_cmd( + """ +clear bgp * + """ + ) + + test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_4) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed to renegotiate with peers" + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf index d82a21e..144466e 100644 --- a/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf @@ -1,24 +1,28 @@ ! +!debug bgp updates +! router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check no bgp default ipv4-unicast - neighbor 192.168.12.2 remote-as external - neighbor 192.168.12.2 timers 1 3 - neighbor 192.168.12.2 timers connect 1 - neighbor 2001:db8::12:2 remote-as external - neighbor 2001:db8::12:2 timers 1 3 - neighbor 2001:db8::12:2 timers connect 1 + neighbor 192.168.12.4 remote-as external + neighbor 192.168.12.4 timers 1 3 + neighbor 192.168.12.4 timers connect 1 + neighbor 2001:db8::12:4 remote-as external + neighbor 2001:db8::12:4 timers 1 3 + neighbor 2001:db8::12:4 timers connect 1 ! address-family ipv4 unicast network 10.0.0.0/31 route-map p1 network 10.0.0.2/32 route-map p2 - neighbor 192.168.12.2 activate + neighbor 192.168.12.4 activate + neighbor 192.168.12.4 addpath-tx-all-paths + network 10.10.10.10/32 exit-address-family address-family ipv6 unicast network 2001:db8::1/128 route-map p1 network 2001:db8:1::/56 route-map p2 - neighbor 2001:db8::12:2 activate + neighbor 2001:db8::12:4 activate exit-address-family ! route-map p1 permit 10 @@ -28,4 +32,3 @@ route-map p2 permit 10 set metric 2 set origin incomplete exit -! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf index cf0013e..55686f4 100644 --- a/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf @@ -5,18 +5,20 @@ router bgp 65002 no bgp ebgp-requires-policy no bgp network import-check no bgp default ipv4-unicast - neighbor 192.168.12.1 remote-as external - neighbor 192.168.12.1 timers 1 3 - neighbor 192.168.12.1 timers connect 1 - neighbor 2001:db8::12:1 remote-as external - neighbor 2001:db8::12:1 timers 1 3 - neighbor 2001:db8::12:1 timers connect 1 + neighbor 192.168.12.4 remote-as external + neighbor 192.168.12.4 timers 1 3 + neighbor 192.168.12.4 timers connect 1 + neighbor 2001:db8::12:4 remote-as external + neighbor 2001:db8::12:4 timers 1 3 + neighbor 2001:db8::12:4 timers connect 1 ! address-family ipv4 unicast - neighbor 192.168.12.1 activate + neighbor 192.168.12.4 activate + neighbor 192.168.12.4 addpath-tx-all-paths + exit-address-family address-family ipv6 unicast - neighbor 2001:db8::12:1 activate + neighbor 2001:db8::12:4 activate exit-address-family ! agentx diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf index 032b93b..f0957cc 100644 --- a/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf @@ -6,6 +6,15 @@ access public_group "" any noauth prefix all all none rocommunity public default +trapsess -v2c -c public 127.0.0.1 + +notificationEvent linkUpTrap linkUp ifIndex ifAdminStatus ifOperStatus +notificationEvent linkDownTrap linkDown ifIndex ifAdminStatus ifOperStatus + +monitor -r 2 -e linkUpTrap "Generate linkUp" ifOperStatus != 2 +monitor -r 2 -e linkDownTrap "Generate linkDown" ifOperStatus == 2 + + view all included .1 iquerySecName frr diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmptrapd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmptrapd.conf new file mode 100644 index 0000000..f6e4abf --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmptrapd.conf @@ -0,0 +1,2 @@ +authCommunity net,log public +disableAuthorization yes diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r3/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r3/bgpd.conf new file mode 100644 index 0000000..71dbda0 --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r3/bgpd.conf @@ -0,0 +1,25 @@ +! +!debug bgp updates +! +router bgp 65003 + no bgp ebgp-requires-policy + no bgp network import-check + no bgp default ipv4-unicast + neighbor 192.168.12.4 remote-as external + neighbor 192.168.12.4 timers 1 3 + neighbor 192.168.12.4 timers connect 1 + neighbor 2001:db8::12:4 remote-as external + neighbor 2001:db8::12:4 timers 1 3 + neighbor 2001:db8::12:4 timers connect 1 + ! + address-family ipv4 unicast + neighbor 192.168.12.4 activate + neighbor 192.168.12.4 addpath-tx-all-paths + network 10.10.10.10/32 + exit-address-family + address-family ipv6 unicast + neighbor 2001:db8::12:4 activate + exit-address-family +! +agentx +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r3/zebra.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r3/zebra.conf new file mode 100644 index 0000000..398af65 --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r3/zebra.conf @@ -0,0 +1,5 @@ +! +interface r3-eth0 + ip address 192.168.12.3/24 + ipv6 address 2001:db8::12:3/64 +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/rr/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/rr/bgpd.conf new file mode 100644 index 0000000..5ebbde6 --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/rr/bgpd.conf @@ -0,0 +1,67 @@ +! +! debug bgp updates +! +router bgp 65004 + no bgp ebgp-requires-policy + no bgp network import-check + no bgp default ipv4-unicast + neighbor 192.168.12.1 remote-as external + neighbor 192.168.12.1 timers 1 3 + neighbor 192.168.12.1 timers connect 1 + neighbor 192.168.12.2 remote-as external + neighbor 192.168.12.2 timers 1 3 + neighbor 192.168.12.2 timers connect 1 + neighbor 192.168.12.3 remote-as external + neighbor 192.168.12.3 timers 1 3 + neighbor 192.168.12.3 timers connect 1 + neighbor 2001:db8::12:1 remote-as external + neighbor 2001:db8::12:1 timers 1 3 + neighbor 2001:db8::12:1 timers connect 1 + neighbor 2001:db8::12:2 remote-as external + neighbor 2001:db8::12:2 timers 1 3 + neighbor 2001:db8::12:2 timers connect 1 + neighbor 2001:db8::12:3 remote-as external + neighbor 2001:db8::12:3 timers 1 3 + neighbor 2001:db8::12:3 timers connect 1 + ! + address-family ipv4 unicast + network 10.0.0.0/31 route-map p1 + network 10.0.0.2/32 route-map p2 + neighbor 192.168.12.1 activate + neighbor 192.168.12.2 activate + neighbor 192.168.12.2 addpath-tx-all-paths + neighbor 192.168.12.2 route-map r2-import in + neighbor 192.168.12.2 route-map r2-export out +! neighbor 192.168.12.2 soft-reconfiguration inbound + neighbor 192.168.12.3 activate + exit-address-family + address-family ipv6 unicast + network 2001:db8::1/128 route-map p1 + network 2001:db8:1::/56 route-map p2 + neighbor 2001:db8::12:1 activate + neighbor 2001:db8::12:2 activate + neighbor 2001:db8::12:2 addpath-tx-all-paths + neighbor 2001:db8::12:3 activate + exit-address-family + + +ip prefix-list r2-toto permit any + +route-map r2-import permit 10 + match ip address prefix-list r2-toto + +route-map r2-export permit 10 + match ip address prefix-list r2-toto +! +route-map p1 permit 10 + set metric 1 +exit +route-map p2 permit 10 + set metric 2 + set origin incomplete +exit + + + +agentx +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/rr/zebra.conf b/tests/topotests/bgp_snmp_bgp4v2mib/rr/zebra.conf new file mode 100644 index 0000000..092673b --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/rr/zebra.conf @@ -0,0 +1,5 @@ +! +interface rr-eth0 + ip address 192.168.12.4/24 + ipv6 address 2001:db8::12:4/64 +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py b/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py index a253d84..c296aaa 100755 --- a/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py +++ b/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py @@ -24,6 +24,7 @@ sys.path.append(os.path.join(CWD, "../")) from lib.topogen import Topogen, TopoRouter, get_topogen from lib.snmptest import SnmpTester from lib import topotest +from lib.topolog import logger pytestmark = [pytest.mark.bgpd, pytest.mark.snmp] @@ -31,10 +32,14 @@ pytestmark = [pytest.mark.bgpd, pytest.mark.snmp] def build_topo(tgen): tgen.add_router("r1") tgen.add_router("r2") + tgen.add_router("r3") + tgen.add_router("rr") 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["rr"]) def setup_module(mod): @@ -55,11 +60,18 @@ def setup_module(mod): os.path.join(CWD, "{}/bgpd.conf".format(rname)), "-M snmp", ) - router.load_config( - TopoRouter.RD_SNMP, - os.path.join(CWD, "{}/snmpd.conf".format(rname)), - "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX", - ) + + r2 = tgen.gears["r2"] + r2.load_config( + TopoRouter.RD_SNMP, + os.path.join(CWD, "{}/snmpd.conf".format(r2.name)), + "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX", + ) + r2.load_config( + TopoRouter.RD_TRAP, + os.path.join(CWD, "{}/snmptrapd.conf".format(r2.name)), + " -On -OQ ", + ) tgen.start_router() @@ -72,28 +84,31 @@ def teardown_module(mod): def test_bgp_snmp_bgp4v2(): tgen = get_topogen() + r1 = tgen.gears["r1"] r2 = tgen.gears["r2"] + rr = tgen.gears["rr"] def _bgp_converge_summary(): output = json.loads(r2.vtysh_cmd("show bgp summary json")) expected = { "ipv4Unicast": { "peers": { - "192.168.12.1": { + "192.168.12.4": { "state": "Established", - "pfxRcd": 2, + "pfxRcd": 6, } } }, "ipv6Unicast": { "peers": { - "2001:db8::12:1": { + "2001:db8::12:4": { "state": "Established", - "pfxRcd": 2, + "pfxRcd": 4, } } }, } + # tgen.mininet_cli() return topotest.json_cmp(output, expected) test_func = functools.partial(_bgp_converge_summary) @@ -136,6 +151,7 @@ def test_bgp_snmp_bgp4v2(): } }, } + # tgen.mininet_cli() return topotest.json_cmp(output, expected) test_func = functools.partial(_bgp_converge_prefixes) @@ -146,11 +162,12 @@ def test_bgp_snmp_bgp4v2(): def _snmpwalk_remote_addr(): expected = { - "1.3.6.1.3.5.1.1.2.1.5.1.1.192.168.12.1": "C0 A8 0C 01", - "1.3.6.1.3.5.1.1.2.1.5.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "20 01 0D B8 00 00 00 00 00 00 00 00 00 12 00 01", + "1.3.6.1.3.5.1.1.2.1.5.1.1.192.168.12.4": "C0 A8 0C 04", + "1.3.6.1.3.5.1.1.2.1.5.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4": "20 01 0D B8 00 00 00 00 00 00 00 00 00 12 00 04", } # bgp4V2PeerRemoteAddr + # tgen.mininet_cli() output, _ = snmp.walk(".1.3.6.1.3.5.1.1.2.1.5") return output == expected @@ -160,8 +177,8 @@ def test_bgp_snmp_bgp4v2(): def _snmpwalk_peer_state(): expected = { - "1.3.6.1.3.5.1.1.2.1.13.1.1.192.168.12.1": "6", - "1.3.6.1.3.5.1.1.2.1.13.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "6", + "1.3.6.1.3.5.1.1.2.1.13.1.1.192.168.12.4": "6", + "1.3.6.1.3.5.1.1.2.1.13.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4": "6", } # bgp4V2PeerState @@ -174,8 +191,8 @@ def test_bgp_snmp_bgp4v2(): def _snmpwalk_peer_last_error_code_received(): expected = { - "1.3.6.1.3.5.1.1.3.1.1.1.1.192.168.12.1": "0", - "1.3.6.1.3.5.1.1.3.1.1.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "0", + "1.3.6.1.3.5.1.1.3.1.1.1.1.192.168.12.4": "0", + "1.3.6.1.3.5.1.1.3.1.1.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4": "0", } # bgp4V2PeerLastErrorCodeReceived @@ -190,10 +207,16 @@ def test_bgp_snmp_bgp4v2(): def _snmpwalk_origin(): expected = { - "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.0.31.1.192.168.12.1.1": "1", - "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.2.32.1.192.168.12.1.1": "3", - "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "1", - "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "3", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.10.10.10.32.1.192.168.12.4.1": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.10.10.10.32.1.192.168.12.4.2": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.0.31.1.192.168.12.4.1": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.0.31.1.192.168.12.4.2": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.2.32.1.192.168.12.4.1": "3", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.2.32.1.192.168.12.4.2": "3", + "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.1": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.2": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.1": "3", + "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.2": "3", } # bgp4V2NlriOrigin @@ -206,10 +229,16 @@ def test_bgp_snmp_bgp4v2(): def _snmpwalk_med(): expected = { - "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.0.31.1.192.168.12.1.1": "1", - "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.2.32.1.192.168.12.1.1": "2", - "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "1", - "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "2", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.10.10.10.32.1.192.168.12.4.1": "0", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.10.10.10.32.1.192.168.12.4.2": "0", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.0.31.1.192.168.12.4.1": "1", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.0.31.1.192.168.12.4.2": "0", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.2.32.1.192.168.12.4.1": "2", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.2.32.1.192.168.12.4.2": "0", + "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.1": "1", + "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.2": "0", + "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.1": "2", + "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.2": "0", } # bgp4V2NlriMed @@ -221,6 +250,68 @@ def test_bgp_snmp_bgp4v2(): assertmsg = "Can't fetch SNMP for bgp4V2NlriMed" assert result, assertmsg + # + # traps + # + + # + # bgp4 traps + # + def _snmptrap_ipv4(): + def __get_notif_bgp4_in_trap_file(router): + snmptrapfile = "{}/{}/snmptrapd.log".format(router.logdir, router.name) + outputfile = open(snmptrapfile).read() + output = snmp.get_notif_bgp4(outputfile) + + return output + + output = __get_notif_bgp4_in_trap_file(r2) + logger.info("output bgp4") + logger.info(output) + return snmp.is_notif_bgp4_valid(output, "192.168.12.4") + + # skip tests is SNMP not installed + if not os.path.isfile("/usr/sbin/snmptrapd"): + error_msg = "SNMP not installed - skipping" + pytest.skip(error_msg) + + rr.vtysh_cmd("clear bgp *") + _, result = topotest.run_and_expect(_snmptrap_ipv4, True, count=30, wait=1) + assertmsg = "Can't fetch SNMP trap for ipv4" + assert result, assertmsg + + # + # bgp4v2 traps + # + def _snmptrap_ipv6(): + def __get_notif_bgp4v2_in_trap_file(router): + snmptrapfile = "{}/{}/snmptrapd.log".format(router.logdir, router.name) + outputfile = open(snmptrapfile).read() + output = snmp.get_notif_bgp4v2(outputfile) + + return output + + # tgen.mininet_cli() + output = __get_notif_bgp4v2_in_trap_file(r2) + logger.info("output bgp4v2") + logger.info(output) + p_ipv4_addr = "1.192.168.12.4" + p_ipv6_addr = "2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4" + return ( + snmp.is_notif_bgp4v2_valid(output, p_ipv4_addr, "Estab") + and snmp.is_notif_bgp4v2_valid(output, p_ipv6_addr, "Estab") + and snmp.is_notif_bgp4v2_valid(output, p_ipv4_addr, "Backward") + and snmp.is_notif_bgp4v2_valid(output, p_ipv6_addr, "Backward") + ) + + sleep(10) + r2.vtysh_cmd("conf\nbgp snmp traps bgp4-mibv2") + r2.vtysh_cmd("conf\nno bgp snmp traps rfc4273") + rr.vtysh_cmd("clear bgp *") + _, result = topotest.run_and_expect(_snmptrap_ipv6, True, count=60, wait=1) + assertmsg = "Can't fetch SNMP trap for ipv6" + assert result, assertmsg + def test_memory_leak(): "Run the memory leak test and report results." diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf index 22b9014..e8c14ab 100644 --- a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65001 diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf index 42b9d51..88de281 100644 --- a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65002 diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf index 339b4eb..443a64a 100644 --- a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65001 diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf index 15779aa..3d9c2cf 100644 --- a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf @@ -4,7 +4,6 @@ hostname pe1 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65001 diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json index 9f78447..2ce936b 100644 --- a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json @@ -12,7 +12,7 @@ { "fib": true, "directlyConnected": true, - "interfaceName": "eth0", + "interfaceName": "vrf10", "vrf": "vrf10", "active": true } diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf index bfc9db9..8805009 100644 --- a/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf index cf31a5c..be43805 100644 --- a/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf index 892a9f7..0770a96 100644 --- a/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf index 9771ee1..70627ad 100644 --- a/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf index d113db1..8079bb0 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf @@ -6,7 +6,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf index 8ccf7a2..c84106f 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! ! debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf index 9a7831d..c2e8530 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf @@ -6,7 +6,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf index 839454d..5c12429 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! ! debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf index 30a0f8f..12b9e8f 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf @@ -6,7 +6,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json index 3cc2fdd..7a4e0d7 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, 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 cbc5ce1..f202493 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf index 7ca2300..db36c18 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf @@ -6,7 +6,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json index 9557054..0dcdec6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, 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 449ca74..9dfed4f 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf index 01b0cc3..57c19e2 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf @@ -6,7 +6,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json index 3cc2fdd..7a4e0d7 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json index eb34333..2050795 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 4, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json index 5517fc7..7a4e0d7 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 6, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json index 25b7a86..0fdd3d6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json index a1f2158..e289df1 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 4, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json index 7eeccd1..0fdd3d6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 6, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, 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 f913b9f..dd8a756 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf index 8700f12..abf4971 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf @@ -6,7 +6,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json index 9557054..0dcdec6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json index d801671..a440ab4 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 4, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json index 25da05b..0dcdec6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 6, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json index 2cd47b9..03bbcc0 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json index f390ef6..5c70cf6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 4, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json index 3353d75..03bbcc0 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 6, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, 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 201d0cc..3c9e4e3 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet 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 f89f337..9fbcdc2 100644 --- a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py +++ b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py @@ -83,6 +83,7 @@ from lib.bgp import ( create_router_bgp, clear_bgp_and_verify, ) +from lib.ospf import verify_ospf_neighbor, clear_ospf # Global variables topo = None @@ -886,6 +887,154 @@ def test_bgp_unique_rid_chaos4_p2(): write_test_footer(tc_name) +def test_bgp_unique_rid_chaos2_p2(): + """ + TC: 8 + 8. Chaos - Verify bgp unique rid functionality when ospf and bgp share the same router ids. + + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + step("Redistribute routes between bgp and ospf.") + reset_config_on_routers(tgen) + + step( + "Base config should be up, verify using BGP convergence on all \ + the routers for IPv4 and IPv6 nbrs" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut="r3") + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "Configure ospf between R3 and R4 with same router ids in both ospf and bgp 10.10.10.10 on R3 BGP and OSPF, and 10.10.10.10 in R4 BGP and 11.11.11.11 in R4 OSPF." + ) + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10"}}, + "r4": {"bgp": {"router_id": "10.10.10.10"}}, + "r5": {"bgp": {"router_id": "10.10.10.10"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "The session should be established between R3 & R4 between BGP process and neighborship should be full between OSPF too." + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut="r3") + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("All the routes should be calculated and installed.") + # Verifying RIB routes + protocol = "bgp" + input_dict = topo["routers"] + verify_rib_rtes = { + "ipv4": { + "r3": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "Null0"}, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "Null0", + } + ] + } + }, + } + dut = "r3" + for addr_type in ADDR_TYPES: + result4 = verify_rib( + tgen, + addr_type, + dut, + verify_rib_rtes, + protocol=protocol, + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Clear ospf process.") + clear_ospf(tgen, "r3") + + step("All the routes should be calculated and installed.") + for addr_type in ADDR_TYPES: + result4 = verify_rib( + tgen, + addr_type, + dut, + verify_rib_rtes, + protocol=protocol, + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Clear bgp process.") + clear_bgp_and_verify(tgen, topo, "r3") + + step("All the routes should be calculated and installed.") + for addr_type in ADDR_TYPES: + result4 = verify_rib( + tgen, + addr_type, + dut, + verify_rib_rtes, + protocol=protocol, + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step( + "Configure ospf between R3 and R5. Configure static routes in R5 and redistribute static routes in ospf on R5." + ) + # Covered as base config. + + step("Verify routes are installed in R3 and R4 route tables.") + dut = "r4" + for addr_type in ADDR_TYPES: + result4 = verify_rib( + tgen, + addr_type, + dut, + verify_rib_rtes, + protocol=protocol, + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + write_test_footer(tc_name) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf index 3bbcc20..473e56b 100644 --- a/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf +++ b/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65500 bgp router-id 192.0.2.1 no bgp ebgp-requires-policy + no bgp enforce-first-as neighbor 192.0.2.100 remote-as 65500 neighbor 192.0.2.100 update-source lo neighbor 192.168.0.100 remote-as 65500 diff --git a/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf index 4c84d52..c7244c0 100644 --- a/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf +++ b/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf @@ -1,9 +1,10 @@ -debug bgp nht -debug bgp zebra -debug bgp labelpool +!debug bgp nht +!debug bgp zebra +!debug bgp labelpool router bgp 65500 bgp router-id 192.0.2.2 no bgp ebgp-requires-policy + no bgp enforce-first-as neighbor 192.0.2.100 remote-as 65500 neighbor 192.0.2.100 update-source lo neighbor 192.168.0.100 remote-as 65500 diff --git a/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf index c5d5727..b7592e4 100644 --- a/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf +++ b/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65501 bgp router-id 192.0.2.3 no bgp ebgp-requires-policy + no bgp enforce-first-as neighbor 192.168.1.200 remote-as 65502 address-family ipv4 unicast no neighbor 192.168.1.200 activate diff --git a/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py index a908e74..39865eb 100644 --- a/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py +++ b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py @@ -48,6 +48,10 @@ sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers from lib import topotest +from lib.bgpcheck import ( + check_show_bgp_vpn_prefix_found, + check_show_bgp_vpn_prefix_not_found, +) from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger from lib.checkping import check_ping @@ -259,62 +263,6 @@ def mpls_table_check_entry(router, out_label, out_nexthop): ) -def check_show_bgp_vpn_prefix_found( - router, ipversion, prefix, rd, label=None, nexthop=None -): - """ - Check if a given vpn prefix is present in the BGP RIB - * 'router': the router to check BGP VPN RIB - * 'ipversion': The ip version to check: ipv4 or ipv6 - * 'prefix': the IP prefix to check - * 'rd': the route distinguisher to check - * 'label: the label to check - """ - output = json.loads( - router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) - ) - if label: - if nexthop: - expected = { - rd: { - "prefix": prefix, - "paths": [{"remoteLabel": label, "nexthops": [{"ip": nexthop}]}], - } - } - else: - expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} - else: - if nexthop: - expected = { - rd: {"prefix": prefix, "paths": [{"nexthops": [{"ip": nexthop}]}]} - } - else: - expected = {rd: {"prefix": prefix}} - return topotest.json_cmp(output, expected) - - -def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None): - """ - Check if a given vpn prefix is not present in the BGP RIB - * 'router': the router to check BGP VPN RIB - * 'ipversion': The ip version to check: ipv4 or ipv6 - * 'prefix': the IP prefix to check - * 'rd': the route distinguisher to check - * 'label: the label to check - """ - output = json.loads( - router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) - ) - if label: - expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} - else: - expected = {rd: {"prefix": prefix}} - ret = topotest.json_cmp(output, expected) - if ret is None: - return "not good" - return None - - def check_show_mpls_table_entry_label_not_found(router, inlabel): output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) expected = {"inLabel": inlabel, "installed": True} diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf index 0249279..d8a45ce 100644 --- a/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf +++ b/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf @@ -1,3 +1,4 @@ +bgp route-map delay-timer 1 router bgp 65500 bgp router-id 192.0.2.1 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py b/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py index 61e1163..8b2e674 100644 --- a/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py +++ b/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py @@ -25,6 +25,10 @@ sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers from lib import topotest +from lib.bgpcheck import ( + check_show_bgp_vpn_prefix_found, + check_show_bgp_vpn_prefix_not_found, +) from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger @@ -214,6 +218,164 @@ def test_protocols_convergence(): assert result is None, assertmsg +def test_export_route_target_empty(): + """ + Check that when removing 'rt vpn export' command, exported prefix is removed + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, Remove 'rt vpn export 52:100' command") + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nno rt vpn export 52:100\n" + ) + + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is removed".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + + +def test_export_route_target_with_routemap_with_export_route_target(): + """ + Check that when removing 'rt vpn export' command, exported prefix is added back + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, configuring route target with route-map with export route target") + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nroute-map vpn export rmap\n" + ) + router.vtysh_cmd( + "configure terminal\nroute-map rmap permit 1\nset extcommunity rt 52:100\n" + ) + + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is added back".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still not present".format(router.name, prefix) + + +def test_export_route_target_with_routemap_without_export_route_target(): + """ + Check that when removing 'set extcommunity rt' command, prefix is removed + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, removing 'set extcommunity rt 52:100.") + router.vtysh_cmd( + "configure terminal\nroute-map rmap permit 1\nno set extcommunity rt\n" + ) + + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is removed".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + + +def test_export_route_target_with_default_command(): + """ + Add back route target with 'rt vpn export' command + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, detach route-map and re-add route target vpn export") + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nrt vpn export 52:100\n" + ) + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is added back".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still not present".format(router.name, prefix) + + +def test_export_suppress_route_target_with_route_map_command(): + """ + Add back route target with 'rt vpn export' command + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, add an extended comm-list to delete 52:100") + + router.vtysh_cmd("configure terminal\nbgp extcommunity-list 1 permit rt 52:100\n") + router.vtysh_cmd( + "configure terminal\nroute-map rmap permit 1\nset extended-comm-list 1 delete\n" + ) + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is removed".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + + +def test_export_add_route_target_to_route_map_command(): + """ + Add route target with route-map so that route is added back + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, add an additional set extcommunity 52:101") + router.vtysh_cmd( + "configure terminal\nroute-map rmap permit 1\nset extcommunity rt 52:101\n" + ) + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is added back".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still not present".format(router.name, prefix) + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py index ce278ed..d4c355a 100644 --- a/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py @@ -151,6 +151,16 @@ def teardown_module(_mod): tgen.stop_topology() +def check_bgp_vpnv4_prefix_presence(router, prefix): + "Check the presence of a prefix" + tgen = get_topogen() + + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) + if not dump: + return "{}, prefix ipv4 vpn {} is not installed yet".format(router.name, prefix) + return None + + def bgp_vpnv4_table_check(router, group, label_list=None, label_value_expected=None): """ Dump and check that vpnv4 entries have the same MPLS label value @@ -163,6 +173,12 @@ def bgp_vpnv4_table_check(router, group, label_list=None, label_value_expected=N stored_label_inited = False for prefix in group: + test_func = functools.partial(check_bgp_vpnv4_prefix_presence, router, prefix) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, prefix ipv4 vpn {} is not installed yet".format( + router.name, prefix + ) + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) assert dump, "{0}, {1}, route distinguisher not present".format( router.name, prefix diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py index e936ccc..3d5f8f6 100644 --- a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py @@ -54,7 +54,7 @@ pytestmark = [pytest.mark.bgpd] PREFIXES_R11 = ["172:31::11/128", "172:31::20/128", "172:31::111/128"] PREFIXES_R12 = ["172:31::12/128", "172:31::15/128"] PREFIXES_REDIST_R14 = ["172:31::14/128"] -PREFIXES_CONNECTED = ["192:168::255/112", "192:2::/64"] +PREFIXES_CONNECTED = ["192:168::255:0/112", "192:2::/64"] def build_topo(tgen): @@ -150,6 +150,16 @@ def teardown_module(_mod): tgen.stop_topology() +def check_bgp_vpnv6_prefix_presence(router, prefix): + "Check the presence of a prefix" + tgen = get_topogen() + + dump = router.vtysh_cmd("show bgp ipv6 vpn {} json".format(prefix), isjson=True) + if not dump: + return "{}, prefix ipv6 vpn {} is not installed yet".format(router.name, prefix) + return None + + def bgp_vpnv6_table_check(router, group, label_list=None, label_value_expected=None): """ Dump and check that vpnv6 entries have the same MPLS label value @@ -162,6 +172,12 @@ def bgp_vpnv6_table_check(router, group, label_list=None, label_value_expected=N stored_label_inited = False for prefix in group: + test_func = functools.partial(check_bgp_vpnv6_prefix_presence, router, prefix) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, prefix ipv6 vpn {} is not installed yet".format( + router.name, prefix + ) + dump = router.vtysh_cmd("show bgp ipv6 vpn {} json".format(prefix), isjson=True) for rd, pathes in dump.items(): for path in pathes["paths"]: @@ -237,7 +253,9 @@ def check_show_mpls_table(router, blacklist=None, label_list=None, whitelist=Non label_list.add(in_label) for nh in label_info["nexthops"]: if "installed" not in nh.keys(): - return "{} {} is not installed yet on {}".format(in_label, label_info, router.name) + return "{} {} is not installed yet on {}".format( + in_label, label_info, router.name + ) if nh["installed"] != True or nh["type"] != "BGP": return "{}, show mpls table, nexthop is not installed".format( router.name diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf index 66493f0..eb2563d 100644 --- a/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf @@ -4,7 +4,6 @@ hostname ce1 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65002 diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf index c5c9927..bbc5ae5 100644 --- a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf @@ -4,7 +4,6 @@ hostname pe1 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65001 diff --git a/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg b/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg index 3260513..11a827a 100644 --- a/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg +++ b/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg @@ -3,7 +3,7 @@ neighbor 10.0.0.1 { local-address 10.0.0.2; local-as 65001; peer-as 65534; - md5 test123; + md5-password test123; static { route 192.168.100.1/32 { diff --git a/tests/topotests/bgp_vrf_netns/exabgp.env b/tests/topotests/bgp_vrf_netns/exabgp.env index a328e04..ec978c6 100644 --- a/tests/topotests/bgp_vrf_netns/exabgp.env +++ b/tests/topotests/bgp_vrf_netns/exabgp.env @@ -1,5 +1,6 @@ [exabgp.api] +ack = false encoder = text highres = false respawn = false diff --git a/tests/topotests/bgp_vrf_netns/peer1/exa-send.py b/tests/topotests/bgp_vrf_netns/peer1/exa-send.py index ab0eb8c..c6a4499 100755 --- a/tests/topotests/bgp_vrf_netns/peer1/exa-send.py +++ b/tests/topotests/bgp_vrf_netns/peer1/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg b/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg index 2d0ca89..97a024c 100644 --- a/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg +++ b/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 1 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 1"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 1 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.101; - local-address 10.0.1.101; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 1; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.101; + local-address 10.0.1.101; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py index 8457b75..028bc35 100644 --- a/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py +++ b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py @@ -94,6 +94,7 @@ def setup_module(module): router.net.set_intf_netns("r1-eth0", ns, up=True) # run daemons + router.load_config(TopoRouter.RD_MGMTD, None, "--vrfwnetns") router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1")), @@ -205,7 +206,6 @@ def test_bgp_vrf_netns(): if __name__ == "__main__": - args = ["-s"] + sys.argv[1:] ret = pytest.main(args) diff --git a/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf b/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf index 03dfbf9..f52f56b 100644 --- a/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf +++ b/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf @@ -1,10 +1,19 @@ hostname r1 +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + import vrf DONNA + ! +! router bgp 99 vrf DONNA no bgp ebgp-requires-policy address-family ipv4 unicast redistribute connected import vrf EVA + import vrf NOTEXISTING + import vrf default ! ! router bgp 99 vrf EVA @@ -12,5 +21,13 @@ router bgp 99 vrf EVA address-family ipv4 unicast redistribute connected import vrf DONNA + import vrf NOTEXISTING + ! +! +router bgp 99 vrf NOTEXISTING + no bgp ebgp-requires-policy + no bgp network import-check + address-family ipv4 unicast + network 172.16.101.0/24 ! ! diff --git a/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf b/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf index 3503855..4de9e89 100644 --- a/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf +++ b/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf @@ -1,5 +1,9 @@ hostname r1 +int dummy0 + ip address 10.0.4.1/24 + no shut +! int dummy1 ip address 10.0.0.1/24 no shut @@ -16,3 +20,9 @@ int dummy4 ip address 10.0.3.1/24 no shut ! +int EVA + no shut +! +int DONNA + no shut +! diff --git a/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs b/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs index fb67953..f62c5cd 100644 --- a/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs +++ b/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs @@ -3,6 +3,7 @@ ip link add DONNA type vrf table 1001 ip link add EVA type vrf table 1002 +ip link add dummy0 type dummy # vrf default ip link add dummy1 type dummy ip link add dummy2 type dummy ip link add dummy3 type dummy diff --git a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py index fd7ffff..ef813e9 100644 --- a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py +++ b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py @@ -64,7 +64,7 @@ def teardown_module(mod): tgen.stop_topology() -def test_vrf_route_leak(): +def test_vrf_route_leak_donna(): logger.info("Ensure that routes are leaked back and forth") tgen = get_topogen() # Don't run this test if we have any failure. @@ -81,11 +81,59 @@ def test_vrf_route_leak(): } ], "10.0.1.0/24": [ - {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, ], "10.0.2.0/24": [{"protocol": "connected"}], "10.0.3.0/24": [ - {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, + ], + "10.0.4.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "lo", + "vrf": "default", + "active": True, + }, + ], + }, + ], + "172.16.101.0/24": [ + { + "protocol": "bgp", + "nexthops": [ + { + "interfaceIndex": 0, + "interfaceName": "unknown", + "vrf": "Unknown", + }, + ], + }, ], } @@ -95,10 +143,31 @@ def test_vrf_route_leak(): result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + +def test_vrf_route_leak_eva(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + # Test EVA VRF. expect = { "10.0.0.0/24": [ - {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, ], "10.0.1.0/24": [ { @@ -106,13 +175,36 @@ def test_vrf_route_leak(): } ], "10.0.2.0/24": [ - {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, ], "10.0.3.0/24": [ { "protocol": "connected", } ], + "172.16.101.0/24": [ + { + "protocol": "bgp", + "nexthops": [ + { + "interfaceIndex": 0, + "interfaceName": "unknown", + "vrf": "Unknown", + }, + ], + }, + ], } test_func = partial( @@ -122,6 +214,217 @@ def test_vrf_route_leak(): assert result, "BGP VRF EVA check failed:\n{}".format(diff) +def test_vrf_route_leak_donna(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Test DONNA VRF. + expect = { + "10.0.0.0/24": [ + { + "protocol": "connected", + } + ], + "10.0.1.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, + ], + "10.0.2.0/24": [{"protocol": "connected"}], + "10.0.3.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, + ], + "10.0.4.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "lo", + "vrf": "default", + "active": True, + }, + ], + }, + ], + "172.16.101.0/24": [ + { + "protocol": "bgp", + "nexthops": [ + { + "interfaceIndex": 0, + "interfaceName": "unknown", + "vrf": "Unknown", + }, + ], + }, + ], + } + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + +def test_vrf_route_leak_eva(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Test EVA VRF. + expect = { + "10.0.0.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.1.0/24": [ + { + "protocol": "connected", + } + ], + "10.0.2.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.3.0/24": [ + { + "protocol": "connected", + } + ], + "172.16.101.0/24": [ + { + "protocol": "bgp", + "nexthops": [ + { + "interfaceIndex": 0, + "interfaceName": "unknown", + "vrf": "Unknown", + }, + ], + }, + ], + } + + +def test_vrf_route_leak_default(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Test default VRF. + expect = { + "10.0.0.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.2.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.4.0/24": [ + { + "protocol": "connected", + } + ], + } + + test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expect) + result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result, "BGP VRF default check failed:\n{}".format(diff) + + +def test_ping(): + "Simple ping tests" + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + logger.info("Ping from default to DONNA") + output = r1.run("ping -c 4 -w 4 -I 10.0.4.1 10.0.0.1") + assert " 0% packet loss" in output, "Ping default->DONNA FAILED" + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index 0afebde..23eab68 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -17,6 +17,7 @@ from pathlib import Path import lib.fixtures import pytest +from lib.common_config import generate_support_bundle from lib.micronet_compat import Mininet from lib.topogen import diagnose_env, get_topogen from lib.topolog import get_test_logdir, logger @@ -103,6 +104,12 @@ def pytest_addoption(parser): ) parser.addoption( + "--gdb-use-emacs", + action="store_true", + help="Use emacsclient to run gdb instead of a shell", + ) + + parser.addoption( "--logd", action="append", metavar="DAEMON[,ROUTER[,...]", @@ -167,6 +174,24 @@ def pytest_addoption(parser): help="Options to pass to `perf record`.", ) + parser.addoption( + "--rr-daemons", + metavar="DAEMON[,DAEMON...]", + help="Comma-separated list of daemons to run `rr` on, or 'all'", + ) + + parser.addoption( + "--rr-routers", + metavar="ROUTER[,ROUTER...]", + help="Comma-separated list of routers to run `rr` on, or 'all'", + ) + + parser.addoption( + "--rr-options", + metavar="OPTS", + help="Options to pass to `rr 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) @@ -203,6 +228,12 @@ def pytest_addoption(parser): ) parser.addoption( + "--valgrind-leak-kinds", + metavar="KIND[,KIND...]", + help="Comma-separated list of valgrind leak kinds or 'all'", + ) + + parser.addoption( "--valgrind-memleaks", action="store_true", help="Run all daemons under valgrind for memleak detection", @@ -568,6 +599,11 @@ def pytest_runtest_setup(item): module = item.parent.module script_dir = os.path.abspath(os.path.dirname(module.__file__)) os.environ["PYTEST_TOPOTEST_SCRIPTDIR"] = script_dir + os.environ["CONFIGDIR"] = script_dir + + +def pytest_exception_interact(node, call, report): + generate_support_bundle() def pytest_runtest_makereport(item, call): diff --git a/tests/topotests/docker/inner/compile_frr.sh b/tests/topotests/docker/inner/compile_frr.sh index 3b296a1..4a88dc6 100755 --- a/tests/topotests/docker/inner/compile_frr.sh +++ b/tests/topotests/docker/inner/compile_frr.sh @@ -64,9 +64,9 @@ if [ ! -e Makefile ]; then --enable-dev-build \ --with-moduledir=/usr/lib/frr/modules \ --prefix=/usr \ - --localstatedir=/var/run/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ --enable-multipath=0 \ --enable-fpm \ --enable-sharpd \ diff --git a/tests/topotests/grpc_basic/test_basic_grpc.py b/tests/topotests/grpc_basic/test_basic_grpc.py index 88bfa98..1ded663 100644 --- a/tests/topotests/grpc_basic/test_basic_grpc.py +++ b/tests/topotests/grpc_basic/test_basic_grpc.py @@ -30,7 +30,7 @@ GRPCP_OSPFD = 50055 GRPCP_PIMD = 50056 pytestmark = [ - # pytest.mark.mgmtd -- Need a new non-protocol marker + pytest.mark.mgmtd, # pytest.mark.bfdd, # pytest.mark.isisd, # pytest.mark.ospfd, @@ -142,7 +142,7 @@ def test_shutdown_checks(tgen): time.sleep(1) try: for r in tgen.routers().values(): - r.net.stopRouter() + r.net.stopRouter(False) r.net.checkRouterCores() finally: if p: diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref index f5d9aa7..8300ca0 100644 --- a/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref +++ b/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref @@ -9,7 +9,7 @@ "adjacencies": { "adjacency": [ { - "neighbor-sys-type": "level-1-2", + "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0004", "hold-timer": 10, "neighbor-priority": 0, @@ -28,7 +28,7 @@ "adjacencies": { "adjacency": [ { - "neighbor-sys-type": "level-1-2", + "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0005", "hold-timer": 10, "neighbor-priority": 0, diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf index 5140eda..92b619e 100644 --- a/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt1 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf index 388348f..3ea5e0a 100644 --- a/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt2 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf index fb45ee1..ea35de8 100644 --- a/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt3 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf index 89837d4..a4b1f2a 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt0 ! !log stdout notifications -!log monitor notifications !log commands ! debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf index 25a9629..f11daa5 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt1 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf index d739a73..ca3235a 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt2 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf index 5c3bed0..9a99910 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt3 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf index 9c00013..5037041 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt4 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf index 61c599d..5f96fd4 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt5 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf index b63401e..23a99a0 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt6 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf index b5a28c7..9be1b48 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt7 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf index dd63f8c..eaaac74 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt8 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf index 378a196..513cf56 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt9 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py index 48e9d53..532e708 100644 --- a/tests/topotests/isis_topo1/test_isis_topo1.py +++ b/tests/topotests/isis_topo1/test_isis_topo1.py @@ -708,9 +708,9 @@ def _check_overload_timer(router, timer_expected): tgen = get_topogen() router = tgen.gears[router] - thread_output = router.vtysh_cmd("show thread timers") + output = router.vtysh_cmd("show event timers") - timer_running = "set_overload_on_start_timer" in thread_output + timer_running = "set_overload_on_start_timer" in output if timer_running == timer_expected: return True return "Expected timer running status: {}".format(timer_expected) diff --git a/tests/topotests/kinds.yaml b/tests/topotests/kinds.yaml index 127790e..5f4b61d 100644 --- a/tests/topotests/kinds.yaml +++ b/tests/topotests/kinds.yaml @@ -12,6 +12,7 @@ kinds: - "./%NAME%:/etc/frr" - "%RUNDIR%/var.log.frr:/var/log/frr" - "%RUNDIR%/var.run.frr:/var/run/frr" + - "%RUNDIR%/var.lib.frr:/var/lib/frr" cap-add: - SYS_ADMIN - AUDIT_WRITE diff --git a/tests/topotests/lib/bgpcheck.py b/tests/topotests/lib/bgpcheck.py new file mode 100644 index 0000000..5ca35a5 --- /dev/null +++ b/tests/topotests/lib/bgpcheck.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0-or-later + +# Copyright 2023, 6wind +import json + +from lib import topotest + + +def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None): + """ + Check if a given vpn prefix is not present in the BGP RIB + * 'router': the router to check BGP VPN RIB + * 'ipversion': The ip version to check: ipv4 or ipv6 + * 'prefix': the IP prefix to check + * 'rd': the route distinguisher to check + * 'label: the label to check + """ + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + expected = {rd: {"prefix": prefix}} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def check_show_bgp_vpn_prefix_found( + router, ipversion, prefix, rd, label=None, nexthop=None +): + """ + Check if a given vpn prefix is present in the BGP RIB + * 'router': the router to check BGP VPN RIB + * 'ipversion': The ip version to check: ipv4 or ipv6 + * 'prefix': the IP prefix to check + * 'rd': the route distinguisher to check + * 'label: the label to check + """ + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + if nexthop: + expected = { + rd: { + "prefix": prefix, + "paths": [{"remoteLabel": label, "nexthops": [{"ip": nexthop}]}], + } + } + else: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + if nexthop: + expected = { + rd: {"prefix": prefix, "paths": [{"nexthops": [{"ip": nexthop}]}]} + } + else: + expected = {rd: {"prefix": prefix}} + return topotest.json_cmp(output, expected) diff --git a/tests/topotests/lib/bmp_collector/bmp.py b/tests/topotests/lib/bmp_collector/bmp.py index b07329c..57f642a 100644 --- a/tests/topotests/lib/bmp_collector/bmp.py +++ b/tests/topotests/lib/bmp_collector/bmp.py @@ -252,6 +252,7 @@ class BMPPerPeerMessage: if peer_type == 0x03: msg['is_filtered'] = bool(peer_flags & IS_FILTERED) + msg['policy'] = 'loc-rib' else: # peer_flags = 0x0000 0000 # ipv6, post-policy, as-path, adj-rib-out, reserverdx4 @@ -259,7 +260,7 @@ class BMPPerPeerMessage: is_as_path = bool(peer_flags & IS_AS_PATH) is_post_policy = bool(peer_flags & IS_POST_POLICY) is_ipv6 = bool(peer_flags & IS_IPV6) - msg['post_policy'] = is_post_policy + msg['policy'] = 'post-policy' if is_post_policy else 'pre-policy' msg['ipv6'] = is_ipv6 msg['peer_ip'] = bin2str_ipaddress(peer_address, is_ipv6) diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index e19d96f..598db84 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -14,6 +14,7 @@ import socket import subprocess import sys import traceback +import configparser from collections import OrderedDict from copy import deepcopy from datetime import datetime, timedelta @@ -21,12 +22,6 @@ from functools import wraps from re import search as re_search from time import sleep -try: - # Imports from python2 - import ConfigParser as configparser -except ImportError: - # Imports from python3 - import configparser from lib.micronet import comm_error from lib.topogen import TopoRouter, get_topogen diff --git a/tests/topotests/lib/fe_client.py b/tests/topotests/lib/fe_client.py new file mode 100755 index 0000000..07059cc --- /dev/null +++ b/tests/topotests/lib/fe_client.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# November 27 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +# noqa: E501 +# +import argparse +import json +import logging +import os +import socket +import struct +import sys +import time +from pathlib import Path + +CWD = os.path.dirname(os.path.realpath(__file__)) + +# This is painful but works if you have installed protobuf would be better if we +# actually built and installed these but ... python packaging. +try: + sys.path.append(os.path.dirname(CWD)) + from munet.base import commander + + commander.cmd_raises(f"protoc --python_out={CWD} -I {CWD}/../../../lib mgmt.proto") +except Exception as error: + logging.error("can't create protobuf definition modules %s", error) + raise + +try: + sys.path[0:0] = "." + import mgmt_pb2 +except Exception as error: + logging.error("can't import proto definition modules %s", error) + raise + +CANDIDATE_DS = mgmt_pb2.DatastoreId.CANDIDATE_DS +OPERATIONAL_DS = mgmt_pb2.DatastoreId.OPERATIONAL_DS +RUNNING_DS = mgmt_pb2.DatastoreId.RUNNING_DS +STARTUP_DS = mgmt_pb2.DatastoreId.STARTUP_DS + +# ===================== +# Native message values +# ===================== + +MGMT_MSG_MARKER_PROTOBUF = b"\000###" +MGMT_MSG_MARKER_NATIVE = b"\001###" + +# +# Native message formats +# +MSG_HDR_FMT = "=H2xIQQ" +HDR_FIELD_CODE = 0 +HDR_FIELD_VSPLIT = 1 +HDR_FIELD_SESS_ID = 2 +HDR_FIELD_REQ_ID = 3 + +MSG_ERROR_FMT = "=h6x" +ERROR_FIELD_ERROR = 0 + +# MSG_GET_TREE_FMT = "=B7x" +# GET_TREE_FIELD_RESULT_TYPE = 0 + +MSG_TREE_DATA_FMT = "=bBB5x" +TREE_DATA_FIELD_PARTIAL_ERROR = 0 +TREE_DATA_FIELD_RESULT_TYPE = 1 +TREE_DATA_FIELD_MORE = 2 + +MSG_GET_DATA_FMT = "=BB6x" +GET_DATA_FIELD_RESULT_TYPE = 0 +GET_DATA_FIELD_FLAGS = 1 +GET_DATA_FLAG_STATE = 0x1 +GET_DATA_FLAG_CONFIG = 0x2 +GET_DATA_FLAG_EXACT = 0x4 + +MSG_NOTIFY_FMT = "=B7x" +NOTIFY_FIELD_RESULT_TYPE = 0 + +# +# Native message codes +# +MSG_CODE_ERROR = 0 +# MSG_CODE_GET_TREE = 1 +MSG_CODE_TREE_DATA = 2 +MSG_CODE_GET_DATA = 3 +MSG_CODE_NOTIFY = 4 + +msg_native_formats = { + MSG_CODE_ERROR: MSG_ERROR_FMT, + # MSG_CODE_GET_TREE: MSG_GET_TREE_FMT, + MSG_CODE_TREE_DATA: MSG_TREE_DATA_FMT, + MSG_CODE_GET_DATA: MSG_GET_DATA_FMT, + MSG_CODE_NOTIFY: MSG_NOTIFY_FMT, +} + + +# Result formats +MSG_FORMAT_XML = 1 +MSG_FORMAT_JSON = 2 +MSG_FORMAT_LYB = 3 + + +def cstr(mdata): + assert mdata[-1] == 0 + return mdata[:-1] + + +class FEClientError(Exception): + pass + + +class PBMessageError(FEClientError): + def __init__(self, msg, errstr): + self.msg = msg + # self.sess_id = mhdr[HDR_FIELD_SESS_ID] + # self.req_id = mhdr[HDR_FIELD_REQ_ID] + self.error = -1 + self.errstr = errstr + super().__init__(f"PBMessageError: {self.errstr}: {msg}") + + +class NativeMessageError(FEClientError): + def __init__(self, mhdr, mfixed, mdata): + self.mhdr = mhdr + self.sess_id = mhdr[HDR_FIELD_SESS_ID] + self.req_id = mhdr[HDR_FIELD_REQ_ID] + self.error = mfixed[0] + self.errstr = cstr(mdata) + super().__init__( + "NativeMessageError: " + f"session {self.sess_id} reqid {self.req_id} " + f"error {self.error}: {self.errstr}" + ) + + +# +# Low-level socket functions +# + + +def recv_wait(sock, size): + """Receive a fixed number of bytes from a stream socket.""" + data = b"" + while len(data) < size: + newdata = sock.recv(size - len(data)) + if not newdata: + raise Exception("Socket closed") + data += newdata + return data + + +def recv_msg(sock): + marker = recv_wait(sock, 4) + assert marker in (MGMT_MSG_MARKER_PROTOBUF, MGMT_MSG_MARKER_NATIVE) + + msize = int.from_bytes(recv_wait(sock, 4), byteorder=sys.byteorder) + assert msize >= 8 + mdata = recv_wait(sock, msize - 8) if msize > 8 else b"" + + return mdata, marker == MGMT_MSG_MARKER_NATIVE + + +def send_msg(sock, marker, mdata): + """Send a mgmtd native message to a stream socket.""" + msize = int.to_bytes(len(mdata) + 8, byteorder=sys.byteorder, length=4) + sock.send(marker) + sock.send(msize) + sock.send(mdata) + + +class Session: + """A session to the mgmtd server.""" + + client_id = 1 + + def __init__(self, sock): + self.sock = sock + self.next_req_id = 1 + + req = mgmt_pb2.FeMessage() + req.register_req.client_name = "test-client" + self.send_pb_msg(req) + logging.debug("Sent FeRegisterReq: %s", req) + + req = mgmt_pb2.FeMessage() + req.session_req.create = 1 + req.session_req.client_conn_id = Session.client_id + Session.client_id += 1 + self.send_pb_msg(req) + logging.debug("Sent FeSessionReq: %s", req) + + reply = self.recv_pb_msg(mgmt_pb2.FeMessage()) + logging.debug("Received FeSessionReply: %s", repr(reply)) + + assert reply.session_reply.success + self.sess_id = reply.session_reply.session_id + + def close(self, clean=True): + if clean: + req = mgmt_pb2.FeMessage() + req.session_req.create = 0 + req.session_req.sess_id = self.sess_id + self.send_pb_msg(req) + self.sock.close() + self.sock = None + + def get_next_req_id(self): + req_id = self.next_req_id + self.next_req_id += 1 + return req_id + + # -------------------------- + # Protobuf message functions + # -------------------------- + + def recv_pb_msg(self, msg): + """Receive a protobuf message.""" + mdata, native = recv_msg(self.sock) + assert not native + + msg.ParseFromString(mdata) + + req = getattr(msg, msg.WhichOneof("message")) + if req.HasField("success"): + if not req.success: + raise PBMessageError(msg, req.error_if_any) + + return msg + + def send_pb_msg(self, msg): + """Send a protobuf message.""" + mdata = msg.SerializeToString() + return send_msg(self.sock, MGMT_MSG_MARKER_PROTOBUF, mdata) + + # ------------------------ + # Native message functions + # ------------------------ + + def recv_native_msg(self): + """Send a native message.""" + mdata, native = recv_msg(self.sock) + assert native + + hlen = struct.calcsize(MSG_HDR_FMT) + hdata = mdata[:hlen] + mhdr = struct.unpack(MSG_HDR_FMT, hdata) + code = mhdr[0] + + if code not in msg_native_formats: + raise Exception(f"Unknown native msg code {code} rcvd") + + mfmt = msg_native_formats[code] + flen = struct.calcsize(mfmt) + fdata = mdata[hlen : hlen + flen] + mfixed = struct.unpack(mfmt, fdata) + mdata = mdata[hlen + flen :] + + if code == MSG_ERROR_FMT: + raise NativeMessageError(mhdr, mfixed, mdata) + + return mhdr, mfixed, mdata + + def send_native_msg(self, mdata): + """Send a native message.""" + return send_msg(self.sock, MGMT_MSG_MARKER_NATIVE, mdata) + + def get_native_msg_header(self, msg_code): + req_id = self.get_next_req_id() + hdata = struct.pack(MSG_HDR_FMT, msg_code, 0, self.sess_id, req_id) + return hdata, req_id + + # ----------------------- + # Front-end API Fountains + # ----------------------- + + def lock(self, lock=True, ds_id=mgmt_pb2.CANDIDATE_DS): + req = mgmt_pb2.FeMessage() + req.lockds_req.session_id = self.sess_id + req.lockds_req.req_id = self.get_next_req_id() + req.lockds_req.ds_id = ds_id + req.lockds_req.lock = lock + self.send_pb_msg(req) + logging.debug("Sent LockDsReq: %s", req) + + reply = self.recv_pb_msg(mgmt_pb2.FeMessage()) + logging.debug("Received Reply: %s", repr(reply)) + assert reply.lockds_reply.success + + def get_data(self, query, data=True, config=False): + # Create the message + mdata, req_id = self.get_native_msg_header(MSG_CODE_GET_DATA) + flags = GET_DATA_FLAG_STATE if data else 0 + flags |= GET_DATA_FLAG_CONFIG if config else 0 + mdata += struct.pack(MSG_GET_DATA_FMT, MSG_FORMAT_JSON, flags) + mdata += query.encode("utf-8") + b"\x00" + + self.send_native_msg(mdata) + logging.debug("Sent GET-TREE") + + mhdr, mfixed, mdata = self.recv_native_msg() + assert mdata[-1] == 0 + result = mdata[:-1].decode("utf-8") + + logging.debug("Received GET: %s: %s", mfixed, mdata) + return result + + # def subscribe(self, notif_xpath): + # # Create the message + # mdata, req_id = self.get_native_msg_header(MSG_CODE_SUBSCRIBE) + # mdata += struct.pack(MSG_SUBSCRIBE_FMT, MSG_FORMAT_JSON) + # mdata += notif_xpath.encode("utf-8") + b"\x00" + + # self.send_native_msg(mdata) + # logging.debug("Sent SUBSCRIBE") + + def recv_notify(self, xpaths=None): + while True: + logging.debug("Waiting for Notify Message") + mhdr, mfixed, mdata = self.recv_native_msg() + if mhdr[HDR_FIELD_CODE] == MSG_CODE_NOTIFY: + logging.debug("Received Notify Message: %s: %s", mfixed, mdata) + else: + raise Exception(f"Received NON-NOTIFY Message: {mfixed}: {mdata}") + + vsplit = mhdr[HDR_FIELD_VSPLIT] + assert mdata[vsplit - 1] == 0 + xpath = mdata[: vsplit - 1].decode("utf-8") + + assert mdata[-1] == 0 + result = mdata[vsplit:-1].decode("utf-8") + + if not xpaths: + return result + js = json.loads(result) + key = [x for x in js.keys()][0] + for xpath in xpaths: + if key.startswith(xpath): + return result + logging.debug("'%s' didn't match xpath filters", key) + + +def __parse_args(): + MPATH = "/var/run/frr/mgmtd_fe.sock" + parser = argparse.ArgumentParser() + parser.add_argument( + "-l", "--listen", nargs="*", metavar="XPATH", help="xpath[s] to listen for" + ) + parser.add_argument( + "--notify-count", + type=int, + default=1, + help="Number of notifications to listen for 0 for infinite", + ) + parser.add_argument( + "-b", "--both", action="store_true", help="return both config and data" + ) + parser.add_argument( + "-c", "--config-only", action="store_true", help="return config only" + ) + parser.add_argument( + "-q", "--query", nargs="+", metavar="XPATH", help="xpath[s] to query" + ) + parser.add_argument("-s", "--server", default=MPATH, help="path to server socket") + parser.add_argument("-v", "--verbose", action="store_true", help="Be verbose") + args = parser.parse_args() + + level = logging.DEBUG if args.verbose else logging.INFO + logging.basicConfig(level=level, format="%(asctime)s %(levelname)s: %(message)s") + + return args + + +def __server_connect(spath): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + logging.debug("Connecting to server on %s", spath) + while ec := sock.connect_ex(str(spath)): + logging.warn("retry server connection in .5s (%s)", os.strerror(ec)) + time.sleep(0.5) + logging.info("Connected to server on %s", spath) + return sock + + +def __main(): + args = __parse_args() + sock = __server_connect(Path(args.server)) + sess = Session(sock) + + if args.query: + # Performa an xpath query + # query = "/frr-interface:lib/interface/state/mtu" + for query in args.query: + logging.info("Sending query: %s", query) + result = sess.get_data( + query, data=not args.config_only, config=(args.both or args.config_only) + ) + print(result) + + if args.listen is not None: + i = args.notify_count + while i > 0 or args.notify_count == 0: + notif = sess.recv_notify(args.listen) + print(notif) + i -= 1 + + +def main(): + try: + __main() + except KeyboardInterrupt: + logging.info("Exiting") + except Exception as error: + logging.error("Unexpected error exiting: %s", error, exc_info=True) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/lib/snmptest.py b/tests/topotests/lib/snmptest.py index e7cd657..814813f 100644 --- a/tests/topotests/lib/snmptest.py +++ b/tests/topotests/lib/snmptest.py @@ -18,6 +18,7 @@ Basic usage instructions: """ from lib.topolog import logger +import re class SnmpTester(object): @@ -72,16 +73,6 @@ class SnmpTester(object): # third token onwards is the value of the object return tokens[0].split(".", 1)[1] - @staticmethod - def _get_snmp_oid(snmp_output): - tokens = snmp_output.strip().split() - - # if len(tokens) > 5: - # return None - - # third token is the value of the object - return tokens[0].split(".", 1)[1] - def _parse_multiline(self, snmp_output): results = snmp_output.strip().split("\n") @@ -116,6 +107,151 @@ class SnmpTester(object): result = self.router.cmd(cmd) return self._parse_multiline(result) + def parse_notif_ipv4(self, notif): + # normalise values + notif = re.sub(":", "", notif) + notif = re.sub('"([0-9]{2}) ([0-9]{2}) "', r"\1\2", notif) + notif = re.sub('"([0-9]{2}) "', r"\1", notif) + elems = re.findall(r"([0-9,\.]+) = ([0-9,\.]+)", notif) + + # remove common part + elems = elems[1:] + return elems + + def is_notif_bgp4_valid(self, output_list, address): + oid_notif_type = ".1.3.6.1.6.3.1.1.4.1.0" + peer_notif_established = ".1.3.6.1.2.1.15.0.1" + peer_notif_backward = ".1.3.6.1.2.1.15.0.2" + oid_peer_last_error = ".1.3.6.1.2.1.15.3.1.14" + oid_peer_remote_addr = ".1.3.6.1.2.1.15.3.1.7" + oid_peer_state = ".1.3.6.1.2.1.15.3.1.2" + + nb_notif = len(output_list) + for nb in range(0, nb_notif - 1): + # identify type of notification + # established or BackwardTransition + + if output_list[nb][0][0] != "{}".format(oid_notif_type): + return False + + if output_list[nb][0][1] == "{}".format(peer_notif_established): + logger.info("Established notification") + elif output_list[nb][0][1] == "{}".format(peer_notif_backward): + logger.info("Backward transition notification") + else: + return False + + # same behavior for 2 notification type in bgp4 + if output_list[nb][1][0] != "{}.{}".format(oid_peer_remote_addr, address): + return False + + if output_list[nb][2][0] != "{}.{}".format(oid_peer_last_error, address): + return False + if output_list[nb][3][0] != "{}.{}".format(oid_peer_state, address): + return False + + return True + + def is_notif_bgp4v2_valid(self, output_list, address, type_requested): + oid_notif_type = ".1.3.6.1.6.3.1.1.4.1.0" + peer_notif_established = ".1.3.6.1.3.5.1.0.1" + peer_notif_backward = ".1.3.6.1.3.5.1.0.2" + oid_peer_state = ".1.3.6.1.3.5.1.1.2.1.13" + oid_peer_local_port = ".1.3.6.1.3.5.1.1.2.1.6" + oid_peer_remote_port = ".1.3.6.1.3.5.1.1.2.1.9" + oid_peer_err_code_recv = ".1.3.6.1.3.5.1.1.3.1.1" + oid_peer_err_sub_code_recv = ".1.3.6.1.3.5.1.1.3.1.2" + oid_peer_err_recv_text = ".1.3.6.1.3.5.1.1.3.1.4" + + nb_notif = len(output_list) + for nb in range(nb_notif): + if output_list[nb][0][0] != "{}".format(oid_notif_type): + return False + + if output_list[nb][0][1] == "{}".format(peer_notif_established): + logger.info("Established notification") + notif_type = "Estab" + + elif output_list[nb][0][1] == "{}".format(peer_notif_backward): + logger.info("Backward transition notification") + notif_type = "Backward" + else: + return False + + if notif_type != type_requested: + continue + + if output_list[nb][1][0] != "{}.1.{}".format(oid_peer_state, address): + continue + + if output_list[nb][2][0] != "{}.1.{}".format(oid_peer_local_port, address): + return False + + if output_list[nb][3][0] != "{}.1.{}".format(oid_peer_remote_port, address): + return False + + if notif_type == "Estab": + return True + + if output_list[nb][4][0] != "{}.1.{}".format( + oid_peer_err_code_recv, address + ): + return False + + if output_list[nb][5][0] != "{}.1.{}".format( + oid_peer_err_sub_code_recv, address + ): + return False + + if output_list[nb][6][0] != "{}.1.{}".format( + oid_peer_err_recv_text, address + ): + return False + + return True + + return False + + def get_notif_bgp4(self, output_file): + notifs = [] + notif_list = [] + whitecleanfile = re.sub("\t", " ", output_file) + results = whitecleanfile.strip().split("\n") + + # don't consider additional SNMP or application messages + for result in results: + if re.search(r"(\.([0-9]+))+\s", result): + notifs.append(result) + + oid_v4 = r"1\.3\.6\.1\.2\.1\.15" + for one_notif in notifs: + is_ipv4_notif = re.search(oid_v4, one_notif) + if is_ipv4_notif != None: + formated_notif = self.parse_notif_ipv4(one_notif) + notif_list.append(formated_notif) + + return notif_list + + def get_notif_bgp4v2(self, output_file): + notifs = [] + notif_list = [] + whitecleanfile = re.sub("\t", " ", output_file) + results = whitecleanfile.strip().split("\n") + + # don't consider additional SNMP or application messages + for result in results: + if re.search(r"(\.([0-9]+))+\s", result): + notifs.append(result) + + oid_v6 = r"1\.3\.6\.1\.3\.5\.1" + for one_notif in notifs: + is_ipv6_notif = re.search(oid_v6, one_notif) + if is_ipv6_notif != None: + formated_notif = self.parse_notif_ipv4(one_notif) + notif_list.append(formated_notif) + + return notif_list + def test_oid(self, oid, value): print("oid: {}".format(self.get_next(oid))) return self.get_next(oid) == value diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 4d935b9..155d2d0 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -81,20 +81,20 @@ def is_string(value): def get_exabgp_cmd(commander=None): - """Return the command to use for ExaBGP version < 4.""" + """Return the command to use for ExaBGP version >= 4.2.11""" if commander is None: commander = Commander("exabgp", logger=logging.getLogger("exabgp")) def exacmd_version_ok(exacmd): - logger.debug("checking %s for exabgp < version 4", exacmd) + logger.debug("checking %s for exabgp version >= 4.2.11", exacmd) _, stdout, _ = commander.cmd_status(exacmd + " -v", warn=False) m = re.search(r"ExaBGP\s*:\s*((\d+)\.(\d+)(?:\.(\d+))?)", stdout) if not m: return False version = m.group(1) - if topotest.version_cmp(version, "4") >= 0: - logging.debug("found exabgp version >= 4 in %s will keep looking", exacmd) + if topotest.version_cmp(version, "4.2.11") < 0: + logging.debug("found exabgp version < 4.2.11 in %s will keep looking", exacmd) return False logger.info("Using ExaBGP version %s in %s", version, exacmd) return True @@ -102,14 +102,14 @@ def get_exabgp_cmd(commander=None): exacmd = commander.get_exec_path("exabgp") if exacmd and exacmd_version_ok(exacmd): return exacmd - py2_path = commander.get_exec_path("python2") - if py2_path: - exacmd = py2_path + " -m exabgp" + py3_path = commander.get_exec_path("python3") + if py3_path: + exacmd = py3_path + " -m exabgp" if exacmd_version_ok(exacmd): return exacmd - py2_path = commander.get_exec_path("python") - if py2_path: - exacmd = py2_path + " -m exabgp" + py3_path = commander.get_exec_path("python") + if py3_path: + exacmd = py3_path + " -m exabgp" if exacmd_version_ok(exacmd): return exacmd return None @@ -719,6 +719,7 @@ class TopoRouter(TopoGear): "/etc/frr", "/etc/snmp", "/var/run/frr", + "/var/lib/frr", "/var/log", ] @@ -744,6 +745,7 @@ class TopoRouter(TopoGear): RD_SNMP = 18 RD_PIM6 = 19 RD_MGMTD = 20 + RD_TRAP = 21 RD = { RD_FRR: "frr", RD_ZEBRA: "zebra", @@ -766,6 +768,7 @@ class TopoRouter(TopoGear): RD_PATH: "pathd", RD_SNMP: "snmpd", RD_MGMTD: "mgmtd", + RD_TRAP: "snmptrapd", } def __init__(self, tgen, cls, name, **params): @@ -842,7 +845,7 @@ class TopoRouter(TopoGear): TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6, TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP, TopoRouter.RD_PIM, TopoRouter.RD_PIM6, TopoRouter.RD_PBR, - TopoRouter.RD_SNMP, TopoRouter.RD_MGMTD. + TopoRouter.RD_SNMP, TopoRouter.RD_MGMTD, TopoRouter.RD_TRAP. Possible `source` values are `None` for an empty config file, a path name which is used directly, or a file name with no path components which is first looked for @@ -880,7 +883,7 @@ class TopoRouter(TopoGear): # Enable all daemon command logging, logging files # and set them to the start dir. for daemon, enabled in nrouter.daemons.items(): - if enabled and daemon != "snmpd": + if enabled and daemon != "snmpd" and daemon != "snmptrapd": self.vtysh_cmd( "\n".join( [ @@ -1196,7 +1199,7 @@ class TopoExaBGP(TopoHost): * Run ExaBGP with env file `env_file` and configuration peer*/exabgp.cfg """ exacmd = self.tgen.get_exabgp_cmd() - assert exacmd, "Can't find a usabel ExaBGP (must be < version 4)" + assert exacmd, "Can't find a usable ExaBGP (must be version >= 4.2.11)" self.run("mkdir -p /etc/exabgp") self.run("chmod 755 /etc/exabgp") @@ -1207,8 +1210,22 @@ class TopoExaBGP(TopoHost): self.run("chmod 644 /etc/exabgp/*") self.run("chmod a+x /etc/exabgp/*.py") self.run("chown -R exabgp:exabgp /etc/exabgp") + self.run("[ -p /var/run/exabgp.in ] || mkfifo /var/run/exabgp.in") + self.run("[ -p /var/run/exabgp.out ] || mkfifo /var/run/exabgp.out") + self.run("chown exabgp:exabgp /var/run/exabgp.{in,out}") + self.run("chmod 600 /var/run/exabgp.{in,out}") + + log_dir = os.path.join(self.logdir, self.name) + self.run("chmod 777 {}".format(log_dir)) + + log_file = os.path.join(log_dir, "exabgp.log") - output = self.run(exacmd + " -e /etc/exabgp/exabgp.env /etc/exabgp/exabgp.cfg") + env_cmd = "env exabgp.log.level=INFO " + env_cmd += "exabgp.log.destination={} ".format(log_file) + + output = self.run( + env_cmd + exacmd + " -e /etc/exabgp/exabgp.env /etc/exabgp/exabgp.cfg " + ) if output is None or len(output) == 0: output = "<none>" @@ -1369,7 +1386,7 @@ def diagnose_env_linux(rundir): logger.info("LDPd tests will not run (missing mpls-iptunnel kernel module)") if not get_exabgp_cmd(): - logger.warning("Failed to find exabgp < 4") + logger.warning("Failed to find exabgp >= 4.2.11") logger.removeHandler(fhandler) fhandler.close() diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index c220bcf..2bb8923 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -31,7 +31,8 @@ from copy import deepcopy import lib.topolog as topolog from lib.micronet_compat import Node from lib.topolog import logger -from munet.base import Timeout +from munet.base import commander, get_exec_path_host, Timeout +from munet.testing.util import retry from lib import micronet @@ -1261,8 +1262,8 @@ 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) @@ -1321,8 +1322,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) @@ -1363,6 +1364,8 @@ def setup_node_tmpdir(logdir, name): class Router(Node): "A Node with IPv4/IPv6 forwarding enabled" + gdb_emacs_router = None + def __init__(self, name, *posargs, **params): # Backward compatibility: # Load configuration defaults like topogen. @@ -1380,6 +1383,8 @@ class Router(Node): ) self.perf_daemons = {} + self.rr_daemons = {} + self.valgrind_gdb_daemons = {} # If this topology is using old API and doesn't have logdir # specified, then attempt to generate an unique logdir. @@ -1420,6 +1425,7 @@ class Router(Node): "pathd": 0, "snmpd": 0, "mgmtd": 0, + "snmptrapd": 0, } self.daemons_options = {"zebra": ""} self.reportCores = True @@ -1799,7 +1805,12 @@ class Router(Node): 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") + gdb_use_emacs = bool(g_pytest_config.option.gdb_use_emacs) + rr_daemons = g_pytest_config.get_option_list("--rr-daemons") + rr_routers = g_pytest_config.get_option_list("--rr-routers") + rr_options = g_pytest_config.get_option("--rr-options", "") valgrind_extra = bool(g_pytest_config.option.valgrind_extra) + valgrind_leak_kinds = g_pytest_config.option.valgrind_leak_kinds valgrind_memleaks = bool(g_pytest_config.option.valgrind_memleaks) strace_daemons = g_pytest_config.get_option_list("--strace-daemons") @@ -1875,6 +1886,15 @@ class Router(Node): # do not since apparently presence of the pidfile impacts BGP GR self.cmd_status("rm -f {0}.pid {0}.vty".format(runbase)) + def do_gdb_or_rr(gdb): + routers = gdb_routers if gdb else rr_routers + daemons = gdb_daemons if gdb else rr_daemons + return ( + (routers or daemons) + and (not routers or self.name in routers or "all" in routers) + and (not daemons or daemon in daemons or "all" in daemons) + ) + rediropt = " > {0}.out 2> {0}.err".format(daemon) if daemon == "snmpd": binary = "/usr/sbin/snmpd" @@ -1883,6 +1903,15 @@ class Router(Node): daemon_opts ) + "{}.pid -x /etc/frr/agentx".format(runbase) # check_daemon_files.append(runbase + ".pid") + elif daemon == "snmptrapd": + binary = "/usr/sbin/snmptrapd" + cmdenv = "" + cmdopt = ( + "{} ".format(daemon_opts) + + "-C -c /etc/{}/snmptrapd.conf".format(self.routertype) + + " -p {}.pid".format(runbase) + + " -LF 6-7 {}/{}/snmptrapd.log".format(self.logdir, self.name) + ) else: binary = os.path.join(self.daemondir, daemon) check_daemon_files.extend([runbase + ".pid", runbase + ".vty"]) @@ -1901,13 +1930,23 @@ class Router(Node): supp_file = os.path.abspath( os.path.join(this_dir, "../../../tools/valgrind.supp") ) - cmdenv += " /usr/bin/valgrind --num-callers=50 --log-file={1}/{2}.valgrind.{0}.%p --leak-check=full --suppressions={3}".format( - daemon, self.logdir, self.name, supp_file + + valgrind_logbase = f"{self.logdir}/{self.name}.valgrind.{daemon}" + if do_gdb_or_rr(True): + cmdenv += " exec" + cmdenv += ( + " /usr/bin/valgrind --num-callers=50" + f" --log-file={valgrind_logbase}.%p" + f" --leak-check=full --suppressions={supp_file}" ) + if valgrind_leak_kinds: + cmdenv += f" --show-leak-kinds={valgrind_leak_kinds}" if valgrind_extra: cmdenv += ( " --gen-suppressions=all --expensive-definedness-checks=yes" ) + if do_gdb_or_rr(True): + cmdenv += " --vgdb-error=0" elif daemon in strace_daemons or "all" in strace_daemons: cmdenv = "strace -f -D -o {1}/{2}.strace.{0} ".format( daemon, self.logdir, self.name @@ -1922,16 +1961,22 @@ class Router(Node): tail_log_files.append( "{}/{}/{}.log".format(self.logdir, self.name, daemon) ) + if extra_opts: cmdopt += " " + extra_opts + if do_gdb_or_rr(True) and do_gdb_or_rr(False): + logger.warning("cant' use gdb and rr at same time") + if ( - (gdb_routers or gdb_daemons) - and ( - not gdb_routers or self.name in gdb_routers or "all" in gdb_routers - ) - and (not gdb_daemons or daemon in gdb_daemons or "all" in gdb_daemons) - ): + not gdb_use_emacs or Router.gdb_emacs_router or valgrind_memleaks + ) and do_gdb_or_rr(True): + if Router.gdb_emacs_router is not None: + logger.warning( + "--gdb-use-emacs can only run a single router and daemon, using" + " new window" + ) + if daemon == "snmpd": cmdopt += " -f " @@ -1941,9 +1986,133 @@ class Router(Node): gdbcmd += " -ex 'set breakpoint pending on'" for bp in gdb_breakpoints: gdbcmd += " -ex 'b {}'".format(bp) - gdbcmd += " -ex 'run {}'".format(cmdopt) - self.run_in_window(gdbcmd, daemon) + if not valgrind_memleaks: + gdbcmd += " -ex 'run {}'".format(cmdopt) + self.run_in_window(gdbcmd, daemon) + + logger.info( + "%s: %s %s launched in gdb window", + self, + self.routertype, + daemon, + ) + + else: + cmd = " ".join([cmdenv, binary, cmdopt]) + p = self.popen(cmd) + self.valgrind_gdb_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, + ) + assert False, "Faled to launch valgrind with gdb" + logger.debug( + "%s: %s %s started with perf", self, self.routertype, daemon + ) + # Now read the erorr log file until we ae given launch priority + timeout = Timeout(30) + vpid = None + for remaining in timeout: + try: + fname = f"{valgrind_logbase}.{p.pid}" + logging.info("Checking %s for valgrind launch info", fname) + o = open(fname, encoding="ascii").read() + except FileNotFoundError: + logging.info("%s not present yet", fname) + else: + m = re.search(r"target remote \| (.*vgdb) --pid=(\d+)", o) + if m: + vgdb_cmd = m.group(0) + break + time.sleep(1) + else: + assert False, "Faled to get launch info for valgrind with gdb" + + gdbcmd += f" -ex '{vgdb_cmd}'" + gdbcmd += " -ex 'c'" + self.run_in_window(gdbcmd, daemon) + + logger.info( + "%s: %s %s launched in gdb window", + self, + self.routertype, + daemon, + ) + elif gdb_use_emacs and do_gdb_or_rr(True): + assert Router.gdb_emacs_router is None + Router.gdb_emacs_router = self + + assert not valgrind_memleaks, "vagrind gdb in emacs not supported yet" + + if daemon == "snmpd": + cmdopt += " -f " + cmdopt += rediropt + + sudo_path = get_exec_path_host("sudo") + ecbin = [ + sudo_path, + "-Eu", + os.environ["SUDO_USER"], + get_exec_path_host("emacsclient"), + ] + pre_cmd = self._get_pre_cmd(True, False, ns_only=True, root_level=True) + # why fail:? gdb -i=mi -iex='set debuginfod enabled off' {binary} " + gdbcmd = f"{sudo_path} {pre_cmd} gdb -i=mi {binary} " + + commander.cmd_raises( + ecbin + + [ + "--eval", + f'(gdb "{gdbcmd}"))', + ] + ) + + elcheck = ( + '(ignore-errors (with-current-buffer "*gud-nsenter*"' + " (and (string-match-p" + ' "(gdb) "' + " (buffer-substring-no-properties " + ' (- (point-max) 10) (point-max))) "ready")))' + ) + + @retry(10) + def emacs_gdb_ready(): + check = commander.cmd_nostatus(ecbin + ["--eval", elcheck]) + return None if "ready" in check else False + + emacs_gdb_ready() + + # target gdb commands + cmd = "set breakpoint pending on" + self.cmd_raises( + ecbin + + [ + "--eval", + f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")', + ] + ) + # gdb breakpoints + for bp in gdb_breakpoints: + self.cmd_raises( + ecbin + + [ + "--eval", + f'(gud-gdb-run-command-fetch-lines "br {bp}" "*gud-gdb*")', + ] + ) + + self.cmd_raises( + ecbin + + [ + "--eval", + f'(gud-gdb-run-command-fetch-lines "run {cmdopt}" "*gud-gdb*")', + ] + ) logger.info( "%s: %s %s launched in gdb window", self, self.routertype, daemon @@ -1969,8 +2138,31 @@ class Router(Node): logger.debug( "%s: %s %s started with perf", self, self.routertype, daemon ) + elif do_gdb_or_rr(False): + cmdopt += rediropt + cmd = " ".join( + [ + "rr record -o {} {} --".format(self.rundir / "rr", rr_options), + binary, + cmdopt, + ] + ) + p = self.popen(cmd) + self.rr_daemons[daemon] = p + if p.poll() and p.returncode: + self.logger.error( + '%s: Failed to launch "%s" (%s) with rr using: %s', + self, + daemon, + p.returncode, + cmd, + ) + else: + logger.debug( + "%s: %s %s started with rr", self, self.routertype, daemon + ) else: - if daemon != "snmpd": + if daemon != "snmpd" and daemon != "snmptrapd": cmdopt += " -d " cmdopt += rediropt @@ -2213,6 +2405,8 @@ class Router(Node): for daemon in self.daemons: if daemon == "snmpd": continue + if daemon == "snmptrapd": + continue if (self.daemons[daemon] == 1) and not (daemon in daemonsRunning): sys.stderr.write("%s: Daemon %s not running\n" % (self.name, daemon)) if daemon == "staticd": diff --git a/tests/topotests/mgmt_config/test_config.py b/tests/topotests/mgmt_config/test_config.py index b07ed8f..1d73222 100644 --- a/tests/topotests/mgmt_config/test_config.py +++ b/tests/topotests/mgmt_config/test_config.py @@ -61,8 +61,7 @@ import pytest from lib.common_config import retry, step from lib.topogen import Topogen, TopoRouter -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] @retry(retry_timeout=1, initial_wait=0.1) diff --git a/tests/topotests/mgmt_config/test_regression.py b/tests/topotests/mgmt_config/test_regression.py index 00c3e01..928151a 100644 --- a/tests/topotests/mgmt_config/test_regression.py +++ b/tests/topotests/mgmt_config/test_regression.py @@ -12,7 +12,7 @@ Test mgmtd regressions import pytest from lib.topogen import Topogen -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] @pytest.fixture(scope="module") @@ -51,3 +51,23 @@ def test_regression_issue_13920(tgen): ) output = r1.net.checkRouterCores() assert not output.strip() + + +def test_regression_pullreq_15423(tgen): + r1 = tgen.gears["r1"] + r1.vtysh_multicmd( + """ + conf t + access-list test seq 1 permit ip any 10.10.10.0 0.0.0.255 + """ + ) + + output = r1.vtysh_multicmd( + """ + conf terminal file-lock + mgmt delete-config /frr-filter:lib/access-list[name='test'][type='ipv4']/entry[sequence='1']/destination-network + mgmt commit apply + end + """ + ) + assert "No changes found" not in output diff --git a/tests/topotests/mgmt_debug_flags/r1/frr.conf b/tests/topotests/mgmt_debug_flags/r1/frr.conf new file mode 100644 index 0000000..ba95b8d --- /dev/null +++ b/tests/topotests/mgmt_debug_flags/r1/frr.conf @@ -0,0 +1,11 @@ +log timestamp precision 6 +log file frr.log + +! debug mgmt backend datastore frontend transaction +! debug mgmt client frontend +! debug mgmt client backend + +interface r1-eth0 + ip address 1.1.1.1/24 +exit +ip route 11.11.11.11/32 1.1.1.2
\ No newline at end of file diff --git a/tests/topotests/mgmt_debug_flags/test_debug.py b/tests/topotests/mgmt_debug_flags/test_debug.py new file mode 100644 index 0000000..e49d9b7 --- /dev/null +++ b/tests/topotests/mgmt_debug_flags/test_debug.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# noqa: E501 +# +""" +Test static route functionality +""" +import pytest +from lib.common_config import step +from lib.topogen import Topogen +from munet.watchlog import WatchLog + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",)} + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + for rname, router in tgen.routers().items(): + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_client_debug_enable(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Start watching log files + watch_mgmtd_log = WatchLog(r1.net.rundir / "mgmtd.log") + watch_staticd_log = WatchLog(r1.net.rundir / "staticd.log") + + def __test_debug(r1, on): + watch_mgmtd_log.snapshot() + watch_staticd_log.snapshot() + + # Add ip route and remove look for debug. + r1.vtysh_cmd("conf t\nip route 11.11.11.11/32 1.1.1.2") + r1.vtysh_cmd("conf t\nno ip route 11.11.11.11/32 1.1.1.2") + + new_mgmt_logs = watch_mgmtd_log.snapshot() + new_be_logs = watch_staticd_log.snapshot() + + fe_cl_msg = "Sending SET_CONFIG_REQ" + fe_ad_msg = "Got SETCFG_REQ" + be_ad_msg = "Sending CFGDATA_CREATE_REQ" + be_cl_msg = "Got CFG_APPLY_REQ" + + if on: + assert fe_cl_msg in new_mgmt_logs + assert fe_ad_msg in new_mgmt_logs + assert be_ad_msg in new_mgmt_logs + assert be_cl_msg in new_be_logs + else: + assert fe_cl_msg not in new_mgmt_logs + assert fe_ad_msg not in new_mgmt_logs + assert be_ad_msg not in new_mgmt_logs + assert be_cl_msg not in new_be_logs + + step("test debug off") + __test_debug(r1, False) + + # Turn it on + step("tests debug on") + r1.vtysh_cmd("debug mgmt client frontend") + r1.vtysh_cmd("debug mgmt client backend") + r1.vtysh_cmd("debug mgmt backend frontend") + __test_debug(r1, True) + + # Turn it off + step("tests debug off") + r1.vtysh_cmd("no debug mgmt client frontend") + r1.vtysh_cmd("no debug mgmt client backend") + r1.vtysh_cmd("no debug mgmt backend frontend") + __test_debug(r1, False) + + # Configure it on + step("tests debug on") + r1.vtysh_cmd("conf t\ndebug mgmt client frontend") + r1.vtysh_cmd("conf t\ndebug mgmt client backend") + r1.vtysh_cmd("conf t\ndebug mgmt backend frontend") + __test_debug(r1, True) + + # Configure it off + step("tests debug off") + r1.vtysh_cmd("conf t\nno debug mgmt client frontend") + r1.vtysh_cmd("conf t\nno debug mgmt client backend") + r1.vtysh_cmd("conf t\nno debug mgmt backend frontend") + __test_debug(r1, False) + + # Turn it on + step("tests debug on") + r1.vtysh_cmd("debug mgmt client frontend") + r1.vtysh_cmd("debug mgmt client backend") + r1.vtysh_cmd("debug mgmt backend frontend") + __test_debug(r1, True) + # Configure it on + step("tests debug on") + r1.vtysh_cmd("conf t\ndebug mgmt client frontend") + r1.vtysh_cmd("conf t\ndebug mgmt client backend") + r1.vtysh_cmd("conf t\ndebug mgmt backend frontend") + __test_debug(r1, True) + # Turn it off + step("tests debug on") + r1.vtysh_cmd("no debug mgmt client frontend") + r1.vtysh_cmd("no debug mgmt client backend") + r1.vtysh_cmd("no debug mgmt backend frontend") + __test_debug(r1, True) + # Configure it off + step("tests debug off") + r1.vtysh_cmd("conf t\nno debug mgmt client frontend") + r1.vtysh_cmd("conf t\nno debug mgmt client backend") + r1.vtysh_cmd("conf t\nno debug mgmt backend frontend") + __test_debug(r1, False) diff --git a/tests/topotests/mgmt_fe_client/mgmt_pb2.py b/tests/topotests/mgmt_fe_client/mgmt_pb2.py new file mode 100644 index 0000000..0aa8803 --- /dev/null +++ b/tests/topotests/mgmt_fe_client/mgmt_pb2.py @@ -0,0 +1,1990 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: mgmt.proto + +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='mgmt.proto', + package='mgmtd', + syntax='proto2', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\nmgmt.proto\x12\x05mgmtd\"\x1e\n\rYangDataXPath\x12\r\n\x05xpath\x18\x01 \x02(\t\"3\n\rYangDataValue\x12\x19\n\x0f\x65ncoded_str_val\x18\x64 \x01(\tH\x00\x42\x07\n\x05value\">\n\x08YangData\x12\r\n\x05xpath\x18\x01 \x02(\t\x12#\n\x05value\x18\x02 \x01(\x0b\x32\x14.mgmtd.YangDataValue\"X\n\x0eYangCfgDataReq\x12\x1d\n\x04\x64\x61ta\x18\x01 \x02(\x0b\x32\x0f.mgmtd.YangData\x12\'\n\x08req_type\x18\x02 \x02(\x0e\x32\x15.mgmtd.CfgDataReqType\"B\n\x0eYangGetDataReq\x12\x1d\n\x04\x64\x61ta\x18\x01 \x02(\x0b\x32\x0f.mgmtd.YangData\x12\x11\n\tnext_indx\x18\x02 \x02(\x03\"R\n\x0e\x42\x65SubscribeReq\x12\x13\n\x0b\x63lient_name\x18\x01 \x02(\t\x12\x18\n\x10subscribe_xpaths\x18\x02 \x02(\x08\x12\x11\n\txpath_reg\x18\x03 \x03(\t\"#\n\x10\x42\x65SubscribeReply\x12\x0f\n\x07success\x18\x01 \x02(\x08\"*\n\x08\x42\x65TxnReq\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\x12\x0e\n\x06\x63reate\x18\x02 \x02(\x08\"=\n\nBeTxnReply\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\x12\x0e\n\x06\x63reate\x18\x02 \x02(\x08\x12\x0f\n\x07success\x18\x03 \x02(\x08\"b\n\x12\x42\x65\x43\x66gDataCreateReq\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\x12\'\n\x08\x64\x61ta_req\x18\x02 \x03(\x0b\x32\x15.mgmtd.YangCfgDataReq\x12\x13\n\x0b\x65nd_of_data\x18\x03 \x02(\x08\"M\n\x14\x42\x65\x43\x66gDataCreateReply\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\x12\x0f\n\x07success\x18\x02 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x03 \x01(\t\"#\n\x11\x42\x65\x43\x66gDataApplyReq\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\"L\n\x13\x42\x65\x43\x66gDataApplyReply\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\x12\x0f\n\x07success\x18\x02 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x03 \x01(\t\"A\n\rYangDataReply\x12\x1d\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x0f.mgmtd.YangData\x12\x11\n\tnext_indx\x18\x02 \x02(\x03\"\x94\x03\n\tBeMessage\x12+\n\nsubscr_req\x18\x02 \x01(\x0b\x32\x15.mgmtd.BeSubscribeReqH\x00\x12/\n\x0csubscr_reply\x18\x03 \x01(\x0b\x32\x17.mgmtd.BeSubscribeReplyH\x00\x12\"\n\x07txn_req\x18\x04 \x01(\x0b\x32\x0f.mgmtd.BeTxnReqH\x00\x12&\n\ttxn_reply\x18\x05 \x01(\x0b\x32\x11.mgmtd.BeTxnReplyH\x00\x12\x31\n\x0c\x63\x66g_data_req\x18\x06 \x01(\x0b\x32\x19.mgmtd.BeCfgDataCreateReqH\x00\x12\x35\n\x0e\x63\x66g_data_reply\x18\x07 \x01(\x0b\x32\x1b.mgmtd.BeCfgDataCreateReplyH\x00\x12\x31\n\rcfg_apply_req\x18\x08 \x01(\x0b\x32\x18.mgmtd.BeCfgDataApplyReqH\x00\x12\x35\n\x0f\x63\x66g_apply_reply\x18\t \x01(\x0b\x32\x1a.mgmtd.BeCfgDataApplyReplyH\x00\x42\t\n\x07message\"$\n\rFeRegisterReq\x12\x13\n\x0b\x63lient_name\x18\x01 \x02(\t\"T\n\x0c\x46\x65SessionReq\x12\x0e\n\x06\x63reate\x18\x01 \x02(\x08\x12\x18\n\x0e\x63lient_conn_id\x18\x02 \x01(\x04H\x00\x12\x14\n\nsession_id\x18\x03 \x01(\x04H\x00\x42\x04\n\x02id\"]\n\x0e\x46\x65SessionReply\x12\x0e\n\x06\x63reate\x18\x01 \x02(\x08\x12\x0f\n\x07success\x18\x02 \x02(\x08\x12\x16\n\x0e\x63lient_conn_id\x18\x03 \x01(\x04\x12\x12\n\nsession_id\x18\x04 \x02(\x04\"b\n\x0b\x46\x65LockDsReq\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12\x0e\n\x06req_id\x18\x02 \x02(\x04\x12!\n\x05\x64s_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0c\n\x04lock\x18\x04 \x02(\x08\"\x8b\x01\n\rFeLockDsReply\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12\x0e\n\x06req_id\x18\x02 \x02(\x04\x12!\n\x05\x64s_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0c\n\x04lock\x18\x04 \x02(\x08\x12\x0f\n\x07success\x18\x05 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x06 \x01(\t\"\xbf\x01\n\x0e\x46\x65SetConfigReq\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12!\n\x05\x64s_id\x18\x02 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x03 \x02(\x04\x12#\n\x04\x64\x61ta\x18\x04 \x03(\x0b\x32\x15.mgmtd.YangCfgDataReq\x12\x17\n\x0fimplicit_commit\x18\x05 \x02(\x08\x12(\n\x0c\x63ommit_ds_id\x18\x06 \x02(\x0e\x32\x12.mgmtd.DatastoreId\"\x99\x01\n\x10\x46\x65SetConfigReply\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12!\n\x05\x64s_id\x18\x02 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x03 \x02(\x04\x12\x0f\n\x07success\x18\x04 \x02(\x08\x12\x17\n\x0fimplicit_commit\x18\x05 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x06 \x01(\t\"\xab\x01\n\x11\x46\x65\x43ommitConfigReq\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12%\n\tsrc_ds_id\x18\x02 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12%\n\tdst_ds_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x04 \x02(\x04\x12\x15\n\rvalidate_only\x18\x05 \x02(\x08\x12\r\n\x05\x61\x62ort\x18\x06 \x02(\x08\"\xd4\x01\n\x13\x46\x65\x43ommitConfigReply\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12%\n\tsrc_ds_id\x18\x02 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12%\n\tdst_ds_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x04 \x02(\x04\x12\x15\n\rvalidate_only\x18\x05 \x02(\x08\x12\x0f\n\x07success\x18\x06 \x02(\x08\x12\r\n\x05\x61\x62ort\x18\x07 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x08 \x01(\t\"\x86\x01\n\x08\x46\x65GetReq\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12\x0e\n\x06\x63onfig\x18\x02 \x02(\x08\x12!\n\x05\x64s_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x04 \x02(\x04\x12#\n\x04\x64\x61ta\x18\x05 \x03(\x0b\x32\x15.mgmtd.YangGetDataReq\"\xae\x01\n\nFeGetReply\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12\x0e\n\x06\x63onfig\x18\x02 \x02(\x08\x12!\n\x05\x64s_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x04 \x02(\x04\x12\x0f\n\x07success\x18\x05 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x06 \x01(\t\x12\"\n\x04\x64\x61ta\x18\x07 \x01(\x0b\x32\x14.mgmtd.YangDataReply\"0\n\x0f\x46\x65NotifyDataReq\x12\x1d\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x0f.mgmtd.YangData\"\x9c\x01\n\x13\x46\x65RegisterNotifyReq\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12!\n\x05\x64s_id\x18\x02 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x14\n\x0cregister_req\x18\x03 \x02(\x08\x12\x0e\n\x06req_id\x18\x04 \x02(\x04\x12(\n\ndata_xpath\x18\x05 \x03(\x0b\x32\x14.mgmtd.YangDataXPath\"\xf0\x04\n\tFeMessage\x12,\n\x0cregister_req\x18\x02 \x01(\x0b\x32\x14.mgmtd.FeRegisterReqH\x00\x12*\n\x0bsession_req\x18\x03 \x01(\x0b\x32\x13.mgmtd.FeSessionReqH\x00\x12.\n\rsession_reply\x18\x04 \x01(\x0b\x32\x15.mgmtd.FeSessionReplyH\x00\x12(\n\nlockds_req\x18\x05 \x01(\x0b\x32\x12.mgmtd.FeLockDsReqH\x00\x12,\n\x0clockds_reply\x18\x06 \x01(\x0b\x32\x14.mgmtd.FeLockDsReplyH\x00\x12+\n\nsetcfg_req\x18\x07 \x01(\x0b\x32\x15.mgmtd.FeSetConfigReqH\x00\x12/\n\x0csetcfg_reply\x18\x08 \x01(\x0b\x32\x17.mgmtd.FeSetConfigReplyH\x00\x12/\n\x0b\x63ommcfg_req\x18\t \x01(\x0b\x32\x18.mgmtd.FeCommitConfigReqH\x00\x12\x33\n\rcommcfg_reply\x18\n \x01(\x0b\x32\x1a.mgmtd.FeCommitConfigReplyH\x00\x12\"\n\x07get_req\x18\x0b \x01(\x0b\x32\x0f.mgmtd.FeGetReqH\x00\x12&\n\tget_reply\x18\x0c \x01(\x0b\x32\x11.mgmtd.FeGetReplyH\x00\x12\x31\n\x0fnotify_data_req\x18\x0f \x01(\x0b\x32\x16.mgmtd.FeNotifyDataReqH\x00\x12\x33\n\rregnotify_req\x18\x10 \x01(\x0b\x32\x1a.mgmtd.FeRegisterNotifyReqH\x00\x42\t\n\x07message*B\n\x0e\x43\x66gDataReqType\x12\x11\n\rREQ_TYPE_NONE\x10\x00\x12\x0c\n\x08SET_DATA\x10\x01\x12\x0f\n\x0b\x44\x45LETE_DATA\x10\x02*`\n\x0b\x44\x61tastoreId\x12\x0b\n\x07\x44S_NONE\x10\x00\x12\x0e\n\nRUNNING_DS\x10\x01\x12\x10\n\x0c\x43\x41NDIDATE_DS\x10\x02\x12\x12\n\x0eOPERATIONAL_DS\x10\x03\x12\x0e\n\nSTARTUP_DS\x10\x04' +) + +_CFGDATAREQTYPE = _descriptor.EnumDescriptor( + name='CfgDataReqType', + full_name='mgmtd.CfgDataReqType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='REQ_TYPE_NONE', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SET_DATA', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DELETE_DATA', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3674, + serialized_end=3740, +) +_sym_db.RegisterEnumDescriptor(_CFGDATAREQTYPE) + +CfgDataReqType = enum_type_wrapper.EnumTypeWrapper(_CFGDATAREQTYPE) +_DATASTOREID = _descriptor.EnumDescriptor( + name='DatastoreId', + full_name='mgmtd.DatastoreId', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DS_NONE', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='RUNNING_DS', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CANDIDATE_DS', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OPERATIONAL_DS', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STARTUP_DS', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3742, + serialized_end=3838, +) +_sym_db.RegisterEnumDescriptor(_DATASTOREID) + +DatastoreId = enum_type_wrapper.EnumTypeWrapper(_DATASTOREID) +REQ_TYPE_NONE = 0 +SET_DATA = 1 +DELETE_DATA = 2 +DS_NONE = 0 +RUNNING_DS = 1 +CANDIDATE_DS = 2 +OPERATIONAL_DS = 3 +STARTUP_DS = 4 + + + +_YANGDATAXPATH = _descriptor.Descriptor( + name='YangDataXPath', + full_name='mgmtd.YangDataXPath', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='xpath', full_name='mgmtd.YangDataXPath.xpath', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=21, + serialized_end=51, +) + + +_YANGDATAVALUE = _descriptor.Descriptor( + name='YangDataValue', + full_name='mgmtd.YangDataValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='encoded_str_val', full_name='mgmtd.YangDataValue.encoded_str_val', index=0, + number=100, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='value', full_name='mgmtd.YangDataValue.value', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=53, + serialized_end=104, +) + + +_YANGDATA = _descriptor.Descriptor( + name='YangData', + full_name='mgmtd.YangData', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='xpath', full_name='mgmtd.YangData.xpath', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', full_name='mgmtd.YangData.value', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=106, + serialized_end=168, +) + + +_YANGCFGDATAREQ = _descriptor.Descriptor( + name='YangCfgDataReq', + full_name='mgmtd.YangCfgDataReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.YangCfgDataReq.data', index=0, + number=1, type=11, cpp_type=10, label=2, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_type', full_name='mgmtd.YangCfgDataReq.req_type', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=170, + serialized_end=258, +) + + +_YANGGETDATAREQ = _descriptor.Descriptor( + name='YangGetDataReq', + full_name='mgmtd.YangGetDataReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.YangGetDataReq.data', index=0, + number=1, type=11, cpp_type=10, label=2, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='next_indx', full_name='mgmtd.YangGetDataReq.next_indx', index=1, + number=2, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=260, + serialized_end=326, +) + + +_BESUBSCRIBEREQ = _descriptor.Descriptor( + name='BeSubscribeReq', + full_name='mgmtd.BeSubscribeReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='client_name', full_name='mgmtd.BeSubscribeReq.client_name', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='subscribe_xpaths', full_name='mgmtd.BeSubscribeReq.subscribe_xpaths', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='xpath_reg', full_name='mgmtd.BeSubscribeReq.xpath_reg', index=2, + number=3, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=328, + serialized_end=410, +) + + +_BESUBSCRIBEREPLY = _descriptor.Descriptor( + name='BeSubscribeReply', + full_name='mgmtd.BeSubscribeReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.BeSubscribeReply.success', index=0, + number=1, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=412, + serialized_end=447, +) + + +_BETXNREQ = _descriptor.Descriptor( + name='BeTxnReq', + full_name='mgmtd.BeTxnReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeTxnReq.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='create', full_name='mgmtd.BeTxnReq.create', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=449, + serialized_end=491, +) + + +_BETXNREPLY = _descriptor.Descriptor( + name='BeTxnReply', + full_name='mgmtd.BeTxnReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeTxnReply.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='create', full_name='mgmtd.BeTxnReply.create', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.BeTxnReply.success', index=2, + number=3, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=493, + serialized_end=554, +) + + +_BECFGDATACREATEREQ = _descriptor.Descriptor( + name='BeCfgDataCreateReq', + full_name='mgmtd.BeCfgDataCreateReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeCfgDataCreateReq.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data_req', full_name='mgmtd.BeCfgDataCreateReq.data_req', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end_of_data', full_name='mgmtd.BeCfgDataCreateReq.end_of_data', index=2, + number=3, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=556, + serialized_end=654, +) + + +_BECFGDATACREATEREPLY = _descriptor.Descriptor( + name='BeCfgDataCreateReply', + full_name='mgmtd.BeCfgDataCreateReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeCfgDataCreateReply.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.BeCfgDataCreateReply.success', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.BeCfgDataCreateReply.error_if_any', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=656, + serialized_end=733, +) + + +_BECFGDATAAPPLYREQ = _descriptor.Descriptor( + name='BeCfgDataApplyReq', + full_name='mgmtd.BeCfgDataApplyReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeCfgDataApplyReq.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=735, + serialized_end=770, +) + + +_BECFGDATAAPPLYREPLY = _descriptor.Descriptor( + name='BeCfgDataApplyReply', + full_name='mgmtd.BeCfgDataApplyReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeCfgDataApplyReply.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.BeCfgDataApplyReply.success', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.BeCfgDataApplyReply.error_if_any', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=772, + serialized_end=848, +) + + +_YANGDATAREPLY = _descriptor.Descriptor( + name='YangDataReply', + full_name='mgmtd.YangDataReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.YangDataReply.data', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='next_indx', full_name='mgmtd.YangDataReply.next_indx', index=1, + number=2, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=850, + serialized_end=915, +) + + +_BEMESSAGE = _descriptor.Descriptor( + name='BeMessage', + full_name='mgmtd.BeMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='subscr_req', full_name='mgmtd.BeMessage.subscr_req', index=0, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='subscr_reply', full_name='mgmtd.BeMessage.subscr_reply', index=1, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='txn_req', full_name='mgmtd.BeMessage.txn_req', index=2, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='txn_reply', full_name='mgmtd.BeMessage.txn_reply', index=3, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cfg_data_req', full_name='mgmtd.BeMessage.cfg_data_req', index=4, + number=6, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cfg_data_reply', full_name='mgmtd.BeMessage.cfg_data_reply', index=5, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cfg_apply_req', full_name='mgmtd.BeMessage.cfg_apply_req', index=6, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cfg_apply_reply', full_name='mgmtd.BeMessage.cfg_apply_reply', index=7, + number=9, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='message', full_name='mgmtd.BeMessage.message', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=918, + serialized_end=1322, +) + + +_FEREGISTERREQ = _descriptor.Descriptor( + name='FeRegisterReq', + full_name='mgmtd.FeRegisterReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='client_name', full_name='mgmtd.FeRegisterReq.client_name', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1324, + serialized_end=1360, +) + + +_FESESSIONREQ = _descriptor.Descriptor( + name='FeSessionReq', + full_name='mgmtd.FeSessionReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='create', full_name='mgmtd.FeSessionReq.create', index=0, + number=1, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_conn_id', full_name='mgmtd.FeSessionReq.client_conn_id', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeSessionReq.session_id', index=2, + number=3, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='id', full_name='mgmtd.FeSessionReq.id', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1362, + serialized_end=1446, +) + + +_FESESSIONREPLY = _descriptor.Descriptor( + name='FeSessionReply', + full_name='mgmtd.FeSessionReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='create', full_name='mgmtd.FeSessionReply.create', index=0, + number=1, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.FeSessionReply.success', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_conn_id', full_name='mgmtd.FeSessionReply.client_conn_id', index=2, + number=3, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeSessionReply.session_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1448, + serialized_end=1541, +) + + +_FELOCKDSREQ = _descriptor.Descriptor( + name='FeLockDsReq', + full_name='mgmtd.FeLockDsReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeLockDsReq.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeLockDsReq.req_id', index=1, + number=2, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeLockDsReq.ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lock', full_name='mgmtd.FeLockDsReq.lock', index=3, + number=4, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1543, + serialized_end=1641, +) + + +_FELOCKDSREPLY = _descriptor.Descriptor( + name='FeLockDsReply', + full_name='mgmtd.FeLockDsReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeLockDsReply.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeLockDsReply.req_id', index=1, + number=2, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeLockDsReply.ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lock', full_name='mgmtd.FeLockDsReply.lock', index=3, + number=4, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.FeLockDsReply.success', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.FeLockDsReply.error_if_any', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1644, + serialized_end=1783, +) + + +_FESETCONFIGREQ = _descriptor.Descriptor( + name='FeSetConfigReq', + full_name='mgmtd.FeSetConfigReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeSetConfigReq.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeSetConfigReq.ds_id', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeSetConfigReq.req_id', index=2, + number=3, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.FeSetConfigReq.data', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='implicit_commit', full_name='mgmtd.FeSetConfigReq.implicit_commit', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='commit_ds_id', full_name='mgmtd.FeSetConfigReq.commit_ds_id', index=5, + number=6, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1786, + serialized_end=1977, +) + + +_FESETCONFIGREPLY = _descriptor.Descriptor( + name='FeSetConfigReply', + full_name='mgmtd.FeSetConfigReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeSetConfigReply.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeSetConfigReply.ds_id', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeSetConfigReply.req_id', index=2, + number=3, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.FeSetConfigReply.success', index=3, + number=4, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='implicit_commit', full_name='mgmtd.FeSetConfigReply.implicit_commit', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.FeSetConfigReply.error_if_any', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1980, + serialized_end=2133, +) + + +_FECOMMITCONFIGREQ = _descriptor.Descriptor( + name='FeCommitConfigReq', + full_name='mgmtd.FeCommitConfigReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeCommitConfigReq.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='src_ds_id', full_name='mgmtd.FeCommitConfigReq.src_ds_id', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='dst_ds_id', full_name='mgmtd.FeCommitConfigReq.dst_ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeCommitConfigReq.req_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='validate_only', full_name='mgmtd.FeCommitConfigReq.validate_only', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='abort', full_name='mgmtd.FeCommitConfigReq.abort', index=5, + number=6, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2136, + serialized_end=2307, +) + + +_FECOMMITCONFIGREPLY = _descriptor.Descriptor( + name='FeCommitConfigReply', + full_name='mgmtd.FeCommitConfigReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeCommitConfigReply.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='src_ds_id', full_name='mgmtd.FeCommitConfigReply.src_ds_id', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='dst_ds_id', full_name='mgmtd.FeCommitConfigReply.dst_ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeCommitConfigReply.req_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='validate_only', full_name='mgmtd.FeCommitConfigReply.validate_only', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.FeCommitConfigReply.success', index=5, + number=6, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='abort', full_name='mgmtd.FeCommitConfigReply.abort', index=6, + number=7, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.FeCommitConfigReply.error_if_any', index=7, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2310, + serialized_end=2522, +) + + +_FEGETREQ = _descriptor.Descriptor( + name='FeGetReq', + full_name='mgmtd.FeGetReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeGetReq.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='config', full_name='mgmtd.FeGetReq.config', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeGetReq.ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeGetReq.req_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.FeGetReq.data', index=4, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2525, + serialized_end=2659, +) + + +_FEGETREPLY = _descriptor.Descriptor( + name='FeGetReply', + full_name='mgmtd.FeGetReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeGetReply.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='config', full_name='mgmtd.FeGetReply.config', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeGetReply.ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeGetReply.req_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.FeGetReply.success', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.FeGetReply.error_if_any', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.FeGetReply.data', index=6, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2662, + serialized_end=2836, +) + + +_FENOTIFYDATAREQ = _descriptor.Descriptor( + name='FeNotifyDataReq', + full_name='mgmtd.FeNotifyDataReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.FeNotifyDataReq.data', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2838, + serialized_end=2886, +) + + +_FEREGISTERNOTIFYREQ = _descriptor.Descriptor( + name='FeRegisterNotifyReq', + full_name='mgmtd.FeRegisterNotifyReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeRegisterNotifyReq.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeRegisterNotifyReq.ds_id', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='register_req', full_name='mgmtd.FeRegisterNotifyReq.register_req', index=2, + number=3, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeRegisterNotifyReq.req_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data_xpath', full_name='mgmtd.FeRegisterNotifyReq.data_xpath', index=4, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2889, + serialized_end=3045, +) + + +_FEMESSAGE = _descriptor.Descriptor( + name='FeMessage', + full_name='mgmtd.FeMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='register_req', full_name='mgmtd.FeMessage.register_req', index=0, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='session_req', full_name='mgmtd.FeMessage.session_req', index=1, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='session_reply', full_name='mgmtd.FeMessage.session_reply', index=2, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lockds_req', full_name='mgmtd.FeMessage.lockds_req', index=3, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lockds_reply', full_name='mgmtd.FeMessage.lockds_reply', index=4, + number=6, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='setcfg_req', full_name='mgmtd.FeMessage.setcfg_req', index=5, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='setcfg_reply', full_name='mgmtd.FeMessage.setcfg_reply', index=6, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='commcfg_req', full_name='mgmtd.FeMessage.commcfg_req', index=7, + number=9, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='commcfg_reply', full_name='mgmtd.FeMessage.commcfg_reply', index=8, + number=10, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='get_req', full_name='mgmtd.FeMessage.get_req', index=9, + number=11, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='get_reply', full_name='mgmtd.FeMessage.get_reply', index=10, + number=12, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='notify_data_req', full_name='mgmtd.FeMessage.notify_data_req', index=11, + number=15, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='regnotify_req', full_name='mgmtd.FeMessage.regnotify_req', index=12, + number=16, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='message', full_name='mgmtd.FeMessage.message', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=3048, + serialized_end=3672, +) + +_YANGDATAVALUE.oneofs_by_name['value'].fields.append( + _YANGDATAVALUE.fields_by_name['encoded_str_val']) +_YANGDATAVALUE.fields_by_name['encoded_str_val'].containing_oneof = _YANGDATAVALUE.oneofs_by_name['value'] +_YANGDATA.fields_by_name['value'].message_type = _YANGDATAVALUE +_YANGCFGDATAREQ.fields_by_name['data'].message_type = _YANGDATA +_YANGCFGDATAREQ.fields_by_name['req_type'].enum_type = _CFGDATAREQTYPE +_YANGGETDATAREQ.fields_by_name['data'].message_type = _YANGDATA +_BECFGDATACREATEREQ.fields_by_name['data_req'].message_type = _YANGCFGDATAREQ +_YANGDATAREPLY.fields_by_name['data'].message_type = _YANGDATA +_BEMESSAGE.fields_by_name['subscr_req'].message_type = _BESUBSCRIBEREQ +_BEMESSAGE.fields_by_name['subscr_reply'].message_type = _BESUBSCRIBEREPLY +_BEMESSAGE.fields_by_name['txn_req'].message_type = _BETXNREQ +_BEMESSAGE.fields_by_name['txn_reply'].message_type = _BETXNREPLY +_BEMESSAGE.fields_by_name['cfg_data_req'].message_type = _BECFGDATACREATEREQ +_BEMESSAGE.fields_by_name['cfg_data_reply'].message_type = _BECFGDATACREATEREPLY +_BEMESSAGE.fields_by_name['cfg_apply_req'].message_type = _BECFGDATAAPPLYREQ +_BEMESSAGE.fields_by_name['cfg_apply_reply'].message_type = _BECFGDATAAPPLYREPLY +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['subscr_req']) +_BEMESSAGE.fields_by_name['subscr_req'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['subscr_reply']) +_BEMESSAGE.fields_by_name['subscr_reply'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['txn_req']) +_BEMESSAGE.fields_by_name['txn_req'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['txn_reply']) +_BEMESSAGE.fields_by_name['txn_reply'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['cfg_data_req']) +_BEMESSAGE.fields_by_name['cfg_data_req'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['cfg_data_reply']) +_BEMESSAGE.fields_by_name['cfg_data_reply'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['cfg_apply_req']) +_BEMESSAGE.fields_by_name['cfg_apply_req'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['cfg_apply_reply']) +_BEMESSAGE.fields_by_name['cfg_apply_reply'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_FESESSIONREQ.oneofs_by_name['id'].fields.append( + _FESESSIONREQ.fields_by_name['client_conn_id']) +_FESESSIONREQ.fields_by_name['client_conn_id'].containing_oneof = _FESESSIONREQ.oneofs_by_name['id'] +_FESESSIONREQ.oneofs_by_name['id'].fields.append( + _FESESSIONREQ.fields_by_name['session_id']) +_FESESSIONREQ.fields_by_name['session_id'].containing_oneof = _FESESSIONREQ.oneofs_by_name['id'] +_FELOCKDSREQ.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FELOCKDSREPLY.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FESETCONFIGREQ.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FESETCONFIGREQ.fields_by_name['data'].message_type = _YANGCFGDATAREQ +_FESETCONFIGREQ.fields_by_name['commit_ds_id'].enum_type = _DATASTOREID +_FESETCONFIGREPLY.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FECOMMITCONFIGREQ.fields_by_name['src_ds_id'].enum_type = _DATASTOREID +_FECOMMITCONFIGREQ.fields_by_name['dst_ds_id'].enum_type = _DATASTOREID +_FECOMMITCONFIGREPLY.fields_by_name['src_ds_id'].enum_type = _DATASTOREID +_FECOMMITCONFIGREPLY.fields_by_name['dst_ds_id'].enum_type = _DATASTOREID +_FEGETREQ.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FEGETREQ.fields_by_name['data'].message_type = _YANGGETDATAREQ +_FEGETREPLY.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FEGETREPLY.fields_by_name['data'].message_type = _YANGDATAREPLY +_FENOTIFYDATAREQ.fields_by_name['data'].message_type = _YANGDATA +_FEREGISTERNOTIFYREQ.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FEREGISTERNOTIFYREQ.fields_by_name['data_xpath'].message_type = _YANGDATAXPATH +_FEMESSAGE.fields_by_name['register_req'].message_type = _FEREGISTERREQ +_FEMESSAGE.fields_by_name['session_req'].message_type = _FESESSIONREQ +_FEMESSAGE.fields_by_name['session_reply'].message_type = _FESESSIONREPLY +_FEMESSAGE.fields_by_name['lockds_req'].message_type = _FELOCKDSREQ +_FEMESSAGE.fields_by_name['lockds_reply'].message_type = _FELOCKDSREPLY +_FEMESSAGE.fields_by_name['setcfg_req'].message_type = _FESETCONFIGREQ +_FEMESSAGE.fields_by_name['setcfg_reply'].message_type = _FESETCONFIGREPLY +_FEMESSAGE.fields_by_name['commcfg_req'].message_type = _FECOMMITCONFIGREQ +_FEMESSAGE.fields_by_name['commcfg_reply'].message_type = _FECOMMITCONFIGREPLY +_FEMESSAGE.fields_by_name['get_req'].message_type = _FEGETREQ +_FEMESSAGE.fields_by_name['get_reply'].message_type = _FEGETREPLY +_FEMESSAGE.fields_by_name['notify_data_req'].message_type = _FENOTIFYDATAREQ +_FEMESSAGE.fields_by_name['regnotify_req'].message_type = _FEREGISTERNOTIFYREQ +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['register_req']) +_FEMESSAGE.fields_by_name['register_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['session_req']) +_FEMESSAGE.fields_by_name['session_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['session_reply']) +_FEMESSAGE.fields_by_name['session_reply'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['lockds_req']) +_FEMESSAGE.fields_by_name['lockds_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['lockds_reply']) +_FEMESSAGE.fields_by_name['lockds_reply'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['setcfg_req']) +_FEMESSAGE.fields_by_name['setcfg_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['setcfg_reply']) +_FEMESSAGE.fields_by_name['setcfg_reply'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['commcfg_req']) +_FEMESSAGE.fields_by_name['commcfg_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['commcfg_reply']) +_FEMESSAGE.fields_by_name['commcfg_reply'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['get_req']) +_FEMESSAGE.fields_by_name['get_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['get_reply']) +_FEMESSAGE.fields_by_name['get_reply'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['notify_data_req']) +_FEMESSAGE.fields_by_name['notify_data_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['regnotify_req']) +_FEMESSAGE.fields_by_name['regnotify_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +DESCRIPTOR.message_types_by_name['YangDataXPath'] = _YANGDATAXPATH +DESCRIPTOR.message_types_by_name['YangDataValue'] = _YANGDATAVALUE +DESCRIPTOR.message_types_by_name['YangData'] = _YANGDATA +DESCRIPTOR.message_types_by_name['YangCfgDataReq'] = _YANGCFGDATAREQ +DESCRIPTOR.message_types_by_name['YangGetDataReq'] = _YANGGETDATAREQ +DESCRIPTOR.message_types_by_name['BeSubscribeReq'] = _BESUBSCRIBEREQ +DESCRIPTOR.message_types_by_name['BeSubscribeReply'] = _BESUBSCRIBEREPLY +DESCRIPTOR.message_types_by_name['BeTxnReq'] = _BETXNREQ +DESCRIPTOR.message_types_by_name['BeTxnReply'] = _BETXNREPLY +DESCRIPTOR.message_types_by_name['BeCfgDataCreateReq'] = _BECFGDATACREATEREQ +DESCRIPTOR.message_types_by_name['BeCfgDataCreateReply'] = _BECFGDATACREATEREPLY +DESCRIPTOR.message_types_by_name['BeCfgDataApplyReq'] = _BECFGDATAAPPLYREQ +DESCRIPTOR.message_types_by_name['BeCfgDataApplyReply'] = _BECFGDATAAPPLYREPLY +DESCRIPTOR.message_types_by_name['YangDataReply'] = _YANGDATAREPLY +DESCRIPTOR.message_types_by_name['BeMessage'] = _BEMESSAGE +DESCRIPTOR.message_types_by_name['FeRegisterReq'] = _FEREGISTERREQ +DESCRIPTOR.message_types_by_name['FeSessionReq'] = _FESESSIONREQ +DESCRIPTOR.message_types_by_name['FeSessionReply'] = _FESESSIONREPLY +DESCRIPTOR.message_types_by_name['FeLockDsReq'] = _FELOCKDSREQ +DESCRIPTOR.message_types_by_name['FeLockDsReply'] = _FELOCKDSREPLY +DESCRIPTOR.message_types_by_name['FeSetConfigReq'] = _FESETCONFIGREQ +DESCRIPTOR.message_types_by_name['FeSetConfigReply'] = _FESETCONFIGREPLY +DESCRIPTOR.message_types_by_name['FeCommitConfigReq'] = _FECOMMITCONFIGREQ +DESCRIPTOR.message_types_by_name['FeCommitConfigReply'] = _FECOMMITCONFIGREPLY +DESCRIPTOR.message_types_by_name['FeGetReq'] = _FEGETREQ +DESCRIPTOR.message_types_by_name['FeGetReply'] = _FEGETREPLY +DESCRIPTOR.message_types_by_name['FeNotifyDataReq'] = _FENOTIFYDATAREQ +DESCRIPTOR.message_types_by_name['FeRegisterNotifyReq'] = _FEREGISTERNOTIFYREQ +DESCRIPTOR.message_types_by_name['FeMessage'] = _FEMESSAGE +DESCRIPTOR.enum_types_by_name['CfgDataReqType'] = _CFGDATAREQTYPE +DESCRIPTOR.enum_types_by_name['DatastoreId'] = _DATASTOREID +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +YangDataXPath = _reflection.GeneratedProtocolMessageType('YangDataXPath', (_message.Message,), { + 'DESCRIPTOR' : _YANGDATAXPATH, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangDataXPath) + }) +_sym_db.RegisterMessage(YangDataXPath) + +YangDataValue = _reflection.GeneratedProtocolMessageType('YangDataValue', (_message.Message,), { + 'DESCRIPTOR' : _YANGDATAVALUE, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangDataValue) + }) +_sym_db.RegisterMessage(YangDataValue) + +YangData = _reflection.GeneratedProtocolMessageType('YangData', (_message.Message,), { + 'DESCRIPTOR' : _YANGDATA, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangData) + }) +_sym_db.RegisterMessage(YangData) + +YangCfgDataReq = _reflection.GeneratedProtocolMessageType('YangCfgDataReq', (_message.Message,), { + 'DESCRIPTOR' : _YANGCFGDATAREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangCfgDataReq) + }) +_sym_db.RegisterMessage(YangCfgDataReq) + +YangGetDataReq = _reflection.GeneratedProtocolMessageType('YangGetDataReq', (_message.Message,), { + 'DESCRIPTOR' : _YANGGETDATAREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangGetDataReq) + }) +_sym_db.RegisterMessage(YangGetDataReq) + +BeSubscribeReq = _reflection.GeneratedProtocolMessageType('BeSubscribeReq', (_message.Message,), { + 'DESCRIPTOR' : _BESUBSCRIBEREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeSubscribeReq) + }) +_sym_db.RegisterMessage(BeSubscribeReq) + +BeSubscribeReply = _reflection.GeneratedProtocolMessageType('BeSubscribeReply', (_message.Message,), { + 'DESCRIPTOR' : _BESUBSCRIBEREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeSubscribeReply) + }) +_sym_db.RegisterMessage(BeSubscribeReply) + +BeTxnReq = _reflection.GeneratedProtocolMessageType('BeTxnReq', (_message.Message,), { + 'DESCRIPTOR' : _BETXNREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeTxnReq) + }) +_sym_db.RegisterMessage(BeTxnReq) + +BeTxnReply = _reflection.GeneratedProtocolMessageType('BeTxnReply', (_message.Message,), { + 'DESCRIPTOR' : _BETXNREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeTxnReply) + }) +_sym_db.RegisterMessage(BeTxnReply) + +BeCfgDataCreateReq = _reflection.GeneratedProtocolMessageType('BeCfgDataCreateReq', (_message.Message,), { + 'DESCRIPTOR' : _BECFGDATACREATEREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeCfgDataCreateReq) + }) +_sym_db.RegisterMessage(BeCfgDataCreateReq) + +BeCfgDataCreateReply = _reflection.GeneratedProtocolMessageType('BeCfgDataCreateReply', (_message.Message,), { + 'DESCRIPTOR' : _BECFGDATACREATEREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeCfgDataCreateReply) + }) +_sym_db.RegisterMessage(BeCfgDataCreateReply) + +BeCfgDataApplyReq = _reflection.GeneratedProtocolMessageType('BeCfgDataApplyReq', (_message.Message,), { + 'DESCRIPTOR' : _BECFGDATAAPPLYREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeCfgDataApplyReq) + }) +_sym_db.RegisterMessage(BeCfgDataApplyReq) + +BeCfgDataApplyReply = _reflection.GeneratedProtocolMessageType('BeCfgDataApplyReply', (_message.Message,), { + 'DESCRIPTOR' : _BECFGDATAAPPLYREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeCfgDataApplyReply) + }) +_sym_db.RegisterMessage(BeCfgDataApplyReply) + +YangDataReply = _reflection.GeneratedProtocolMessageType('YangDataReply', (_message.Message,), { + 'DESCRIPTOR' : _YANGDATAREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangDataReply) + }) +_sym_db.RegisterMessage(YangDataReply) + +BeMessage = _reflection.GeneratedProtocolMessageType('BeMessage', (_message.Message,), { + 'DESCRIPTOR' : _BEMESSAGE, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeMessage) + }) +_sym_db.RegisterMessage(BeMessage) + +FeRegisterReq = _reflection.GeneratedProtocolMessageType('FeRegisterReq', (_message.Message,), { + 'DESCRIPTOR' : _FEREGISTERREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeRegisterReq) + }) +_sym_db.RegisterMessage(FeRegisterReq) + +FeSessionReq = _reflection.GeneratedProtocolMessageType('FeSessionReq', (_message.Message,), { + 'DESCRIPTOR' : _FESESSIONREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeSessionReq) + }) +_sym_db.RegisterMessage(FeSessionReq) + +FeSessionReply = _reflection.GeneratedProtocolMessageType('FeSessionReply', (_message.Message,), { + 'DESCRIPTOR' : _FESESSIONREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeSessionReply) + }) +_sym_db.RegisterMessage(FeSessionReply) + +FeLockDsReq = _reflection.GeneratedProtocolMessageType('FeLockDsReq', (_message.Message,), { + 'DESCRIPTOR' : _FELOCKDSREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeLockDsReq) + }) +_sym_db.RegisterMessage(FeLockDsReq) + +FeLockDsReply = _reflection.GeneratedProtocolMessageType('FeLockDsReply', (_message.Message,), { + 'DESCRIPTOR' : _FELOCKDSREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeLockDsReply) + }) +_sym_db.RegisterMessage(FeLockDsReply) + +FeSetConfigReq = _reflection.GeneratedProtocolMessageType('FeSetConfigReq', (_message.Message,), { + 'DESCRIPTOR' : _FESETCONFIGREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeSetConfigReq) + }) +_sym_db.RegisterMessage(FeSetConfigReq) + +FeSetConfigReply = _reflection.GeneratedProtocolMessageType('FeSetConfigReply', (_message.Message,), { + 'DESCRIPTOR' : _FESETCONFIGREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeSetConfigReply) + }) +_sym_db.RegisterMessage(FeSetConfigReply) + +FeCommitConfigReq = _reflection.GeneratedProtocolMessageType('FeCommitConfigReq', (_message.Message,), { + 'DESCRIPTOR' : _FECOMMITCONFIGREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeCommitConfigReq) + }) +_sym_db.RegisterMessage(FeCommitConfigReq) + +FeCommitConfigReply = _reflection.GeneratedProtocolMessageType('FeCommitConfigReply', (_message.Message,), { + 'DESCRIPTOR' : _FECOMMITCONFIGREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeCommitConfigReply) + }) +_sym_db.RegisterMessage(FeCommitConfigReply) + +FeGetReq = _reflection.GeneratedProtocolMessageType('FeGetReq', (_message.Message,), { + 'DESCRIPTOR' : _FEGETREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeGetReq) + }) +_sym_db.RegisterMessage(FeGetReq) + +FeGetReply = _reflection.GeneratedProtocolMessageType('FeGetReply', (_message.Message,), { + 'DESCRIPTOR' : _FEGETREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeGetReply) + }) +_sym_db.RegisterMessage(FeGetReply) + +FeNotifyDataReq = _reflection.GeneratedProtocolMessageType('FeNotifyDataReq', (_message.Message,), { + 'DESCRIPTOR' : _FENOTIFYDATAREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeNotifyDataReq) + }) +_sym_db.RegisterMessage(FeNotifyDataReq) + +FeRegisterNotifyReq = _reflection.GeneratedProtocolMessageType('FeRegisterNotifyReq', (_message.Message,), { + 'DESCRIPTOR' : _FEREGISTERNOTIFYREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeRegisterNotifyReq) + }) +_sym_db.RegisterMessage(FeRegisterNotifyReq) + +FeMessage = _reflection.GeneratedProtocolMessageType('FeMessage', (_message.Message,), { + 'DESCRIPTOR' : _FEMESSAGE, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeMessage) + }) +_sym_db.RegisterMessage(FeMessage) + + +# @@protoc_insertion_point(module_scope) diff --git a/tests/topotests/mgmt_fe_client/oper.py b/tests/topotests/mgmt_fe_client/oper.py new file mode 120000 index 0000000..9244392 --- /dev/null +++ b/tests/topotests/mgmt_fe_client/oper.py @@ -0,0 +1 @@ +../mgmt_oper/oper.py
\ No newline at end of file diff --git a/tests/topotests/mgmt_fe_client/r1/frr.conf b/tests/topotests/mgmt_fe_client/r1/frr.conf new file mode 100644 index 0000000..cf8ba16 --- /dev/null +++ b/tests/topotests/mgmt_fe_client/r1/frr.conf @@ -0,0 +1,23 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +interface r1-eth0 + ip address 1.1.1.1/24 +exit + +interface r1-eth1 vrf red + ip address 3.3.3.1/24 +exit +ip route 11.11.11.11/32 1.1.1.2 +!ip route 13.13.13.13/32 3.3.3.2 vrf red
\ No newline at end of file diff --git a/tests/topotests/mgmt_fe_client/test_client.py b/tests/topotests/mgmt_fe_client/test_client.py new file mode 100644 index 0000000..b5a74c6 --- /dev/null +++ b/tests/topotests/mgmt_fe_client/test_client.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# noqa: E501 +# +""" +Test static route functionality +""" +import pytest +from lib.topogen import Topogen +from oper import check_kernel_32 + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.add_loop("lo-red") + router.net.attach_iface_to_l3vrf("lo-red", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth1", "red") + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_oper_simple(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + check_kernel_32(r1, "11.11.11.11", 1, "") diff --git a/tests/topotests/mgmt_notif/oper.py b/tests/topotests/mgmt_notif/oper.py new file mode 120000 index 0000000..9244392 --- /dev/null +++ b/tests/topotests/mgmt_notif/oper.py @@ -0,0 +1 @@ +../mgmt_oper/oper.py
\ No newline at end of file diff --git a/tests/topotests/mgmt_notif/r1/frr.conf b/tests/topotests/mgmt_notif/r1/frr.conf new file mode 100644 index 0000000..47e7395 --- /dev/null +++ b/tests/topotests/mgmt_notif/r1/frr.conf @@ -0,0 +1,27 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +ip route 11.11.11.11/32 lo + +interface r1-eth0 + ip address 1.1.1.1/24 + ip rip authentication string foo + ip rip authentication mode text +exit + +router rip + network 1.1.1.0/24 + timers basic 5 15 10 + redistribute static +exit diff --git a/tests/topotests/mgmt_notif/r2/frr.conf b/tests/topotests/mgmt_notif/r2/frr.conf new file mode 100644 index 0000000..cd05201 --- /dev/null +++ b/tests/topotests/mgmt_notif/r2/frr.conf @@ -0,0 +1,27 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +ip route 22.22.22.22/32 lo + +interface r2-eth0 + ip address 1.1.1.2/24 + ip rip authentication string bar + ip rip authentication mode text +exit + +router rip + network 1.1.1.0/24 + timers basic 5 15 10 + redistribute static +exit
\ No newline at end of file diff --git a/tests/topotests/mgmt_notif/test_notif.py b/tests/topotests/mgmt_notif/test_notif.py new file mode 100644 index 0000000..c85e7ba --- /dev/null +++ b/tests/topotests/mgmt_notif/test_notif.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# January 23 2024, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2024, LabN Consulting, L.L.C. +# + +""" +Test YANG Notifications +""" +import json +import logging +import os + +import pytest +from lib.topogen import Topogen +from lib.topotest import json_cmp +from oper import check_kernel_32 + +pytestmark = [pytest.mark.ripd, pytest.mark.staticd, pytest.mark.mgmtd] + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = { + "s1": ("r1", "r2"), + } + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_frontend_notification(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + + check_kernel_32(r1, "11.11.11.11", 1, "") + + fe_client_path = CWD + "/../lib/fe_client.py" + rc, _, _ = r1.cmd_status(fe_client_path + " --help") + + if rc: + pytest.skip("No protoc or present cannot run test") + + # The first notifications is a frr-ripd:authentication-type-failure + # So we filter to avoid that, all the rest are frr-ripd:authentication-failure + # making our test deterministic + output = r1.cmd_raises( + fe_client_path + " --listen frr-ripd:authentication-failure" + ) + jsout = json.loads(output) + + expected = {"frr-ripd:authentication-failure": {"interface-name": "r1-eth0"}} + result = json_cmp(jsout, expected) + assert result is None + + output = r1.cmd_raises(fe_client_path + " --listen") + jsout = json.loads(output) + + expected = {"frr-ripd:authentication-failure": {"interface-name": "r1-eth0"}} + result = json_cmp(jsout, expected) + assert result is None + + +def test_backend_notification(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + + check_kernel_32(r1, "11.11.11.11", 1, "") + + be_client_path = "/usr/lib/frr/mgmtd_testc" + rc, _, _ = r1.cmd_status(be_client_path + " --help") + + if rc: + pytest.skip("No mgmtd_testc") + + output = r1.cmd_raises( + be_client_path + " --timeout 20 --log file:mgmt_testc.log --listen /frr-ripd" + ) + + jsout = json.loads(output) + + expected = {"frr-ripd:authentication-failure": {"interface-name": "r1-eth0"}} + result = json_cmp(jsout, expected) + assert result is None diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-default.json b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-default.json new file mode 100644 index 0000000..435d733 --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-default.json @@ -0,0 +1,576 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-nokey.json b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-nokey.json new file mode 100644 index 0000000..1a1f648 --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-nokey.json @@ -0,0 +1,1145 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + }, + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "13.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "13.13.13.13/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "3.3.3.2", + "interface": "r1-eth2", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "14.14.14.14/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "4.4.4.2", + "interface": "r1-eth3", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-red.json b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-red.json new file mode 100644 index 0000000..cfabd49 --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-red.json @@ -0,0 +1,576 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "13.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "13.13.13.13/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "3.3.3.2", + "interface": "r1-eth2", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "14.14.14.14/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "4.4.4.2", + "interface": "r1-eth3", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra-ribs.json b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra-ribs.json new file mode 100644 index 0000000..2e2b8ec --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra-ribs.json @@ -0,0 +1,572 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra.json b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra.json new file mode 100644 index 0000000..2e2b8ec --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra.json @@ -0,0 +1,572 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib.json b/tests/topotests/mgmt_oper/oper-results/result-lib.json new file mode 100644 index 0000000..1a1f648 --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib.json @@ -0,0 +1,1145 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + }, + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "13.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "13.13.13.13/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "3.3.3.2", + "interface": "r1-eth2", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "14.14.14.14/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "4.4.4.2", + "interface": "r1-eth3", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-ipv4-unicast.json b/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-ipv4-unicast.json new file mode 100644 index 0000000..956d3a8 --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-ipv4-unicast.json @@ -0,0 +1,225 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-nokeys.json b/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-nokeys.json new file mode 100644 index 0000000..2e2b8ec --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-nokeys.json @@ -0,0 +1,572 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper.py b/tests/topotests/mgmt_oper/oper.py new file mode 100644 index 0000000..0f6c3cd --- /dev/null +++ b/tests/topotests/mgmt_oper/oper.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# October 29 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# + +import datetime +import ipaddress +import json +import logging +import math +import os +import pprint +import re + +from lib.common_config import retry, step +from lib.topolog import logger +from lib.topotest import json_cmp as tt_json_cmp + +try: + from deepdiff import DeepDiff as dd_json_cmp +except ImportError: + dd_json_cmp = None + + +def json_cmp(got, expect, exact_match): + if dd_json_cmp: + if exact_match: + deep_diff = dd_json_cmp(expect, got) + # Convert DeepDiff completely into dicts or lists at all levels + json_diff = json.loads(deep_diff.to_json()) + else: + json_diff = dd_json_cmp(expect, got, ignore_order=True) + # Convert DeepDiff completely into dicts or lists at all levels + # json_diff = json.loads(deep_diff.to_json()) + # Remove new fields in json object from diff + if json_diff.get("dictionary_item_added") is not None: + del json_diff["dictionary_item_added"] + # Remove new json objects in json array from diff + if (new_items := json_diff.get("iterable_item_added")) is not None: + new_item_paths = list(new_items.keys()) + for path in new_item_paths: + if type(new_items[path]) is dict: + del new_items[path] + if len(new_items) == 0: + del json_diff["iterable_item_added"] + if not json_diff: + json_diff = None + else: + json_diff = tt_json_cmp(got, expect, exact_match) + json_diff = str(json_diff) + return json_diff + + +def enable_debug(router): + router.vtysh_cmd("debug northbound callbacks configuration") + + +def disable_debug(router): + router.vtysh_cmd("no debug northbound callbacks configuration") + + +def do_oper_test(tgen, query_results): + r1 = tgen.gears["r1"].net + + qcmd = ( + r"vtysh -c 'show mgmt get-data {} {}' " + r"""| sed -e 's/"phy-address": ".*"/"phy-address": "rubout"/'""" + r"""| sed -e 's/"uptime": ".*"/"uptime": "rubout"/'""" + r"""| sed -e 's/"vrf": "[0-9]*"/"vrf": "rubout"/'""" + r"""| sed -e 's/"if-index": [0-9][0-9]*/"if-index": "rubout"/'""" + r"""| sed -e 's/"id": [0-9][0-9]*/"id": "rubout"/'""" + ) + + doreset = True + dd_json_cmp = None + for qr in query_results: + step(f"Perform query '{qr[0]}'", reset=doreset) + if doreset: + doreset = False + expected = open(qr[1], encoding="ascii").read() + output = r1.cmd_nostatus(qcmd.format(qr[0], qr[2] if len(qr) > 2 else "")) + + try: + ojson = json.loads(output) + except json.decoder.JSONDecodeError as error: + logging.error("Error decoding json: %s\noutput:\n%s", error, output) + raise + + try: + ejson = json.loads(expected) + except json.decoder.JSONDecodeError as error: + logging.error( + "Error decoding json exp result: %s\noutput:\n%s", error, expected + ) + raise + + if dd_json_cmp: + cmpout = json_cmp(ojson, ejson, exact_match=True) + if cmpout: + logging.warning( + "-------DIFF---------\n%s\n---------DIFF----------", + pprint.pformat(cmpout), + ) + else: + cmpout = tt_json_cmp(ojson, ejson, exact=True) + if cmpout: + logging.warning( + "-------EXPECT--------\n%s\n------END-EXPECT------", + json.dumps(ejson, indent=4), + ) + logging.warning( + "--------GOT----------\n%s\n-------END-GOT--------", + json.dumps(ojson, indent=4), + ) + + assert cmpout is None + + +def get_ip_networks(super_prefix, count): + count_log2 = math.log(count, 2) + if count_log2 != int(count_log2): + count_log2 = int(count_log2) + 1 + else: + count_log2 = int(count_log2) + network = ipaddress.ip_network(super_prefix) + return tuple(network.subnets(count_log2))[0:count] + + +@retry(retry_timeout=30, initial_wait=0.1) +def check_kernel(r1, super_prefix, count, add, is_blackhole, vrf, matchvia): + network = ipaddress.ip_network(super_prefix) + vrfstr = f" vrf {vrf}" if vrf else "" + if network.version == 6: + kernel = r1.cmd_raises(f"ip -6 route show{vrfstr}") + else: + kernel = r1.cmd_raises(f"ip -4 route show{vrfstr}") + + # logger.debug("checking kernel routing table%s:\n%s", vrfstr, kernel) + + for i, net in enumerate(get_ip_networks(super_prefix, count)): + if not add: + assert str(net) not in kernel + continue + + if is_blackhole: + route = f"blackhole {str(net)} proto (static|196) metric 20" + else: + route = ( + f"{str(net)}(?: nhid [0-9]+)? {matchvia} " + "proto (static|196) metric 20" + ) + assert re.search(route, kernel), f"Failed to find \n'{route}'\n in \n'{kernel}'" + + +def addrgen(a, count, step=1): + for _ in range(0, count, step): + yield a + a += step + + +@retry(retry_timeout=30, initial_wait=0.1) +def check_kernel_32(r1, start_addr, count, vrf, step=1): + start = ipaddress.ip_address(start_addr) + vrfstr = f" vrf {vrf}" if vrf else "" + if start.version == 6: + kernel = r1.cmd_raises(f"ip -6 route show{vrfstr}") + else: + kernel = r1.cmd_raises(f"ip -4 route show{vrfstr}") + + nentries = len(re.findall("\n", kernel)) + logging.info("checking kernel routing table%s: (%s entries)", vrfstr, nentries) + + for addr in addrgen(start, count, step): + assert str(addr) in kernel, f"Failed to find '{addr}' in {nentries} entries" + + +def do_config( + r1, + count, + add=True, + do_ipv6=False, + via=None, + vrf=None, + use_cli=False, +): + optype = "adding" if add else "removing" + iptype = "IPv6" if do_ipv6 else "IPv4" + + # + # Set the route details + # + + if vrf: + super_prefix = "2111::/48" if do_ipv6 else "111.0.0.0/8" + else: + super_prefix = "2055::/48" if do_ipv6 else "55.0.0.0/8" + + matchvia = "" + if via == "blackhole": + pass + elif via: + matchvia = f"dev {via}" + else: + if vrf: + via = "2102::2" if do_ipv6 else "3.3.3.2" + matchvia = f"via {via} dev r1-eth1" + else: + via = "2101::2" if do_ipv6 else "1.1.1.2" + matchvia = f"via {via} dev r1-eth0" + + vrfdbg = " in vrf {}".format(vrf) if vrf else "" + logger.debug("{} {} static {} routes{}".format(optype, count, iptype, vrfdbg)) + + # + # Generate config file in a retrievable place + # + + config_file = os.path.join( + r1.logdir, r1.name, "{}-routes-{}.conf".format(iptype.lower(), optype) + ) + with open(config_file, "w") as f: + if use_cli: + f.write("configure terminal\n") + if vrf: + f.write("vrf {}\n".format(vrf)) + + for i, net in enumerate(get_ip_networks(super_prefix, count)): + if add: + f.write("ip route {} {}\n".format(net, via)) + else: + f.write("no ip route {} {}\n".format(net, via)) + + # + # Load config file. + # + + if use_cli: + load_command = 'vtysh < "{}"'.format(config_file) + else: + load_command = 'vtysh -f "{}"'.format(config_file) + tstamp = datetime.datetime.now() + output = r1.cmd_raises(load_command) + delta = (datetime.datetime.now() - tstamp).total_seconds() + + # + # Verify the results are in the kernel + # + check_kernel(r1, super_prefix, count, add, via == "blackhole", vrf, matchvia) + + optyped = "added" if add else "removed" + logger.debug( + "{} {} {} static routes under {}{} in {}s".format( + optyped, count, iptype.lower(), super_prefix, vrfdbg, delta + ) + ) diff --git a/tests/topotests/mgmt_oper/r1/frr-scale.conf b/tests/topotests/mgmt_oper/r1/frr-scale.conf new file mode 100644 index 0000000..237d013 --- /dev/null +++ b/tests/topotests/mgmt_oper/r1/frr-scale.conf @@ -0,0 +1,25 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +! debug northbound libyang +! debug northbound callbacks + +debug northbound notifications +debug northbound events + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +interface r1-eth0 + ip address 1.1.1.1/24 +exit + +interface r1-eth1 vrf red + ip address 3.3.3.1/24 +exit + +ip route 11.11.11.11/32 1.1.1.2 +ip route 13.13.13.13/32 3.3.3.2 vrf red
\ No newline at end of file diff --git a/tests/topotests/mgmt_oper/r1/frr-simple.conf b/tests/topotests/mgmt_oper/r1/frr-simple.conf new file mode 100644 index 0000000..73df6b9 --- /dev/null +++ b/tests/topotests/mgmt_oper/r1/frr-simple.conf @@ -0,0 +1,26 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +interface r1-eth0 + ip address 1.1.1.1/24 + description r1-eth0-desc + evpn mh es-df-pref 32767 +exit + +interface r1-eth1 vrf red + ip address 3.3.3.1/24 + description r1-eth1-desc +exit +ip route 11.11.11.11/32 1.1.1.2 +!ip route 13.13.13.13/32 3.3.3.2 vrf red diff --git a/tests/topotests/mgmt_oper/r1/frr.conf b/tests/topotests/mgmt_oper/r1/frr.conf new file mode 100644 index 0000000..72a67bf --- /dev/null +++ b/tests/topotests/mgmt_oper/r1/frr.conf @@ -0,0 +1,41 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +interface r1-eth0 + ip address 1.1.1.1/24 + ipv6 address 2001:1111::1/64 +exit + +interface r1-eth1 + ip address 2.2.2.1/24 + ipv6 address 2002:2222::1/64 +exit + +interface r1-eth2 vrf red + ip address 3.3.3.1/24 + ipv6 address 2003:333::1/64 +exit + +interface r1-eth3 vrf red + ip address 4.4.4.1/24 + ipv6 address 2004:4444::1/64 +exit + +ip route 11.0.0.0/8 Null0 +ip route 11.11.11.11/32 1.1.1.2 +ip route 12.12.12.12/32 2.2.2.2 + +ip route 13.0.0.0/8 Null0 vrf red +ip route 13.13.13.13/32 3.3.3.2 vrf red +ip route 14.14.14.14/32 4.4.4.2 vrf red
\ No newline at end of file diff --git a/tests/topotests/mgmt_oper/simple-results/result-empty.json b/tests/topotests/mgmt_oper/simple-results/result-empty.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-empty.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-description.json b/tests/topotests/mgmt_oper/simple-results/result-intf-description.json new file mode 100644 index 0000000..8f8092e --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-description.json @@ -0,0 +1,14 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "description": "r1-eth0-desc" + }, + { + "name": "r1-eth1", + "description": "r1-eth1-desc" + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-description-exact.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-description-exact.json new file mode 100644 index 0000000..e00f23c --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-description-exact.json @@ -0,0 +1,3 @@ +{ + "frr-interface:description": "r1-eth0-desc" +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-exact.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-exact.json new file mode 100644 index 0000000..f04e3a5 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-exact.json @@ -0,0 +1,22 @@ +{ + "frr-interface:interface": [ + { + "name": "r1-eth0", + "vrf": "default", + "state": { + "if-index": "rubout", + "mtu": 1500, + "mtu6": 1500, + "speed": 10000, + "metric": 0, + "phy-address": "rubout" + }, + "frr-zebra:zebra": { + "state": { + "up-count": 0, + "down-count": 0 + } + } + } + ] +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-name.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-name.json new file mode 100644 index 0000000..3988204 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-name.json @@ -0,0 +1,9 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0" + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-only-config.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-only-config.json new file mode 100644 index 0000000..e48002e --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-only-config.json @@ -0,0 +1,21 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "description": "r1-eth0-desc", + "frr-zebra:zebra": { + "ipv4-addrs": [ + { + "ip": "1.1.1.1", + "prefix-length": 24 + } + ], + "evpn-mh": { + "df-preference": 32767 + } + } + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-vrf.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-vrf.json new file mode 100644 index 0000000..3a6eeb8 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-vrf.json @@ -0,0 +1,10 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "vrf": "default" + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all-tag.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all-tag.json new file mode 100644 index 0000000..caee164 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all-tag.json @@ -0,0 +1,13 @@ +{ + "frr-zebra:evpn-mh": { + "df-preference": 32767, + "bypass": false, + "@bypass": { + "ietf-netconf-with-defaults:default": true + }, + "uplink": false, + "@uplink": { + "ietf-netconf-with-defaults:default": true + } + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all.json new file mode 100644 index 0000000..07ba53b --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all.json @@ -0,0 +1,7 @@ +{ + "frr-zebra:evpn-mh": { + "df-preference": 32767, + "bypass": false, + "uplink": false + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-explicit.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-explicit.json new file mode 100644 index 0000000..1779d1c --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-explicit.json @@ -0,0 +1,5 @@ +{ + "frr-zebra:evpn-mh": { + "df-preference": 32767 + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-trim.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-trim.json new file mode 100644 index 0000000..efd7e8c --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-trim.json @@ -0,0 +1,3 @@ +{ + "frr-zebra:evpn-mh": {} +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-with-config.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-with-config.json new file mode 100644 index 0000000..84ad82c --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-with-config.json @@ -0,0 +1,34 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "vrf": "default", + "description": "r1-eth0-desc", + "state": { + "if-index": "rubout", + "mtu": 1500, + "mtu6": 1500, + "speed": 10000, + "metric": 0, + "phy-address": "rubout" + }, + "frr-zebra:zebra": { + "ipv4-addrs": [ + { + "ip": "1.1.1.1", + "prefix-length": 24 + } + ], + "evpn-mh": { + "df-preference": 32767 + }, + "state": { + "up-count": 0, + "down-count": 0 + } + } + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-name.json b/tests/topotests/mgmt_oper/simple-results/result-intf-name.json new file mode 100644 index 0000000..9d8ea75 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-name.json @@ -0,0 +1,21 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "lo" + }, + { + "name": "r1-eth0" + }, + { + "name": "lo-red" + }, + { + "name": "r1-eth1" + }, + { + "name": "red" + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-state-mtu.json b/tests/topotests/mgmt_oper/simple-results/result-intf-state-mtu.json new file mode 100644 index 0000000..6035971 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-state-mtu.json @@ -0,0 +1,12 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "state": { + "mtu": 1500 + } + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-state.json b/tests/topotests/mgmt_oper/simple-results/result-intf-state.json new file mode 100644 index 0000000..981df02 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-state.json @@ -0,0 +1,17 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "state": { + "if-index": "rubout", + "mtu": 1500, + "mtu6": 1500, + "speed": 10000, + "metric": 0, + "phy-address": "rubout" + } + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-default.json b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-default.json new file mode 100644 index 0000000..cea4bf5 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-default.json @@ -0,0 +1,193 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-nokey.json b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-nokey.json new file mode 100644 index 0000000..75414ca --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-nokey.json @@ -0,0 +1,350 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + }, + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-red.json b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-red.json new file mode 100644 index 0000000..0538231 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-red.json @@ -0,0 +1,164 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra-ribs.json b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra-ribs.json new file mode 100644 index 0000000..4f40820 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra-ribs.json @@ -0,0 +1,189 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra.json b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra.json new file mode 100644 index 0000000..4f40820 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra.json @@ -0,0 +1,189 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib.json b/tests/topotests/mgmt_oper/simple-results/result-lib.json new file mode 100644 index 0000000..75414ca --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib.json @@ -0,0 +1,350 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + }, + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-ipv4-unicast.json b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-ipv4-unicast.json new file mode 100644 index 0000000..7ce60c3 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-ipv4-unicast.json @@ -0,0 +1,110 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-nokeys.json b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-nokeys.json new file mode 100644 index 0000000..4f40820 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-nokeys.json @@ -0,0 +1,189 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-nokey.json b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-nokey.json new file mode 100644 index 0000000..7ce60c3 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-nokey.json @@ -0,0 +1,110 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-prefix.json b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-prefix.json new file mode 100644 index 0000000..833d418 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-prefix.json @@ -0,0 +1,50 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-singleton-metric.json b/tests/topotests/mgmt_oper/simple-results/result-singleton-metric.json new file mode 100644 index 0000000..b3a7df5 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-singleton-metric.json @@ -0,0 +1,30 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "metric": 0 + } + ] + } + ] + } + ] + } + } + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/test_oper.py b/tests/topotests/mgmt_oper/test_oper.py new file mode 100644 index 0000000..6971eaf --- /dev/null +++ b/tests/topotests/mgmt_oper/test_oper.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +""" +Test static route functionality +""" + +import ipaddress +import math +import time + +import pytest +from lib.topogen import Topogen +from oper import check_kernel_32, do_oper_test + +try: + from deepdiff import DeepDiff as dd_json_cmp +except ImportError: + dd_json_cmp = None + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",), "s3": ("r1",), "s4": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.add_loop("lo-red") + router.net.attach_iface_to_l3vrf("lo-red", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth2", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth3", "red") + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def get_ip_networks(super_prefix, count): + count_log2 = math.log(count, 2) + if count_log2 != int(count_log2): + count_log2 = int(count_log2) + 1 + else: + count_log2 = int(count_log2) + network = ipaddress.ip_network(super_prefix) + return tuple(network.subnets(count_log2))[0:count] + + +def test_oper(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + query_results = [ + ("/frr-vrf:lib", "oper-results/result-lib.json"), + ("/frr-vrf:lib/vrf", "oper-results/result-lib-vrf-nokey.json"), + ( + '/frr-vrf:lib/vrf[name="default"]', + "oper-results/result-lib-vrf-default.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra', + "oper-results/result-lib-vrf-zebra.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs', + "oper-results/result-lib-vrf-zebra-ribs.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib', + "oper-results/result-ribs-rib-nokeys.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]', + "oper-results/result-ribs-rib-ipv4-unicast.json", + ), + ] + + r1 = tgen.gears["r1"].net + check_kernel_32(r1, "11.11.11.11", 1, "") + check_kernel_32(r1, "12.12.12.12", 1, "") + check_kernel_32(r1, "13.13.13.13", 1, "red") + check_kernel_32(r1, "14.14.14.14", 1, "red") + time.sleep(2) + do_oper_test(tgen, query_results) + + +to_gen_new_results = """ +scriptdir=~chopps/w/frr/tests/topotests/mgmt_oper +resdir=${scriptdir}/oper-results +vtysh -c 'show mgmt get-data /frr-vrf:lib' > ${resdir}/result-lib.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf' > ${resdir}/result-lib-vrf-nokey.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]' > ${resdir}/result-lib-vrf-default.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="red"]' > ${resdir}/result-lib-vrf-red.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra' > ${resdir}/result-lib-vrf-zebra.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs' > ${resdir}/result-lib-vrf-zebra-ribs.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib' > ${resdir}/result-ribs-rib-nokeys.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]' > ${resdir}/result-ribs-rib-ipv4-unicast.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route' > ${resdir}/result-ribs-rib-route-nokey.json + +for f in ${resdir}/result-*; do + sed -i -e 's/"uptime": ".*"/"uptime": "rubout"/;s/"id": [0-9][0-9]*/"id": "rubout"/' $f + sed -i -e 's/"if-index": [0-9][0-9]*/"if-index": "rubout"/' $f + sed -i -e 's,"vrf": "[0-9]*","vrf": "rubout",' $f +done +""" # noqa: 501 +# should not differ +# diff result-lib.json result-lib-vrf-nokey.json +# diff result-lib-vrf-zebra.json result-lib-vrf-zebra-ribs.json diff --git a/tests/topotests/mgmt_oper/test_querying.py b/tests/topotests/mgmt_oper/test_querying.py new file mode 100644 index 0000000..086a87b --- /dev/null +++ b/tests/topotests/mgmt_oper/test_querying.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# noqa: E501 +# +""" +Test various query types +""" +import json +import logging + +import pytest +from lib.common_config import step +from lib.topogen import Topogen +from oper import check_kernel_32 + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.add_loop("lo-red") + router.net.attach_iface_to_l3vrf("lo-red", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth1", "red") + router.load_frr_config("frr-simple.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_oper_simple(tgen): + """This test is useful for doing manual testing""" + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + query_results = [ + # Non-key specific query with function filtering selector + '/frr-interface:lib/interface[contains(name,"eth")]/vrf', + # Non-key specific query with child value filtering selector + '/frr-interface:lib/interface[vrf="red"]/vrf', + '/frr-interface:lib/interface[./vrf="red"]/vrf', + # Container query with function filtering selector + '/frr-interface:lib/interface[contains(name,"eth")]/state', + # Multi list elemenet with function filtering selector + '/frr-interface:lib/interface[contains(name,"eth")]', + # + # Specific list entry after non-specific lists + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route/route-entry[protocol="connected"]', + # crashes: All specific until the end, then walk + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.1.0/24"]/route-entry[protocol="connected"]', + # Does nothing: Root level query + "//metric", + # specific leaf after non-specific lists + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + "route/route-entry/metric", + # All specific until the end generic. + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.1.0/24"]/route-entry', + # All specific until the penultimate generic with a specific leaf child. + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.1.0/24"]/route-entry/metric', + # All generic until the end (middle) specific with unspecified + # children below to walk. + '/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route[prefix="1.1.1.0/24"]', + # All generic until the end which is a specific leaf. + "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/metric", + ] + # query_results = [ + # '/frr-interface:lib/frr-interface:interface/frr-zebra:zebra/ip-addrs[frr-rt:address-family="frr-rt:ipv4"][prefix="1.1.1.1/24"]' + # ] + + r1 = tgen.gears["r1"].net + check_kernel_32(r1, "11.11.11.11", 1, "") + + step("Oper test start", reset=True) + + for qr in query_results: + step(f"Perform query '{qr}'") + try: + output = r1.cmd_nostatus(f"vtysh -c 'show mgmt get-data {qr}'") + except Exception as error: + logging.error("Error sending query: %s: %s", qr, error) + continue + + try: + ojson = json.loads(output) + logging.info("'%s': generates:\n%s", qr, ojson) + except json.decoder.JSONDecodeError as error: + logging.error("Error decoding json: %s\noutput:\n%s", error, output) diff --git a/tests/topotests/mgmt_oper/test_scale.py b/tests/topotests/mgmt_oper/test_scale.py new file mode 100644 index 0000000..41c2b8c --- /dev/null +++ b/tests/topotests/mgmt_oper/test_scale.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# noqa: E501 +# +""" +Test static route functionality +""" +import re +import time + +import pytest +from lib.common_config import step +from lib.topogen import Topogen, TopoRouter +from oper import check_kernel_32 + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.add_loop("lo-red") + router.net.attach_iface_to_l3vrf("lo-red", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth1", "red") + router.load_frr_config("frr-scale.conf") + router.load_config(TopoRouter.RD_SHARP, "") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_oper_simple(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + + time.sleep(2) + count = 20 * 1000 + + vrf = None # "red" + check_kernel_32(r1, "11.11.11.11", 1, vrf) + + step("Found 11.11.11.11 in kernel adding sharpd routes") + r1.cmd_raises(f"vtysh -c 'sharp install routes 20.0.0.0 nexthop 1.1.1.2 {count}'") + check_kernel_32(r1, "20.0.0.0", count, vrf, 1000) + + step(f"All {count} routes installed in kernel, continuing") + # output = r1.cmd_raises("vtysh -c 'show mgmt get-data /frr-vrf:lib'") + # step(f"Got output: {output[0:1024]}") + + query = '/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route[contains(prefix,"20.0.0.12")]/prefix' + output = r1.cmd_raises(f"vtysh -c 'show mgmt get-data {query}'") + matches = re.findall(r'"prefix":', output) + # 20.0.0.12 + 20.0.0.12{0,1,2,3,4,5,6,7,8,9} + assert len(matches) == 11 diff --git a/tests/topotests/mgmt_oper/test_simple.py b/tests/topotests/mgmt_oper/test_simple.py new file mode 100644 index 0000000..3b115f6 --- /dev/null +++ b/tests/topotests/mgmt_oper/test_simple.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# noqa: E501 +# +""" +Test static route functionality +""" +import pytest +from lib.topogen import Topogen +from oper import check_kernel_32, do_oper_test + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.add_loop("lo-red") + router.net.attach_iface_to_l3vrf("lo-red", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth1", "red") + router.load_frr_config("frr-simple.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_oper_simple(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + query_results = [ + ( + # Non-key query with key specific selection + '/frr-interface:lib/interface[name="r1-eth0"]/vrf', + "simple-results/result-intf-eth0-vrf.json", + ), + # Test machines will have different sets of interfaces so the test results will + # vary and need to be generated dynamically before this test is re-enabled + # ( + # # Key query on generic list + # "/frr-interface:lib/interface/name", + # "simple-results/result-intf-name.json", + # ), + ( + # Key query with key specific selection + '/frr-interface:lib/interface[name="r1-eth0"]/name', + "simple-results/result-intf-eth0-name.json", + ), + ("/frr-vrf:lib", "simple-results/result-lib.json"), + ("/frr-vrf:lib/vrf", "simple-results/result-lib-vrf-nokey.json"), + ( + '/frr-vrf:lib/vrf[name="default"]', + "simple-results/result-lib-vrf-default.json", + ), + ('/frr-vrf:lib/vrf[name="red"]', "simple-results/result-lib-vrf-red.json"), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra', + "simple-results/result-lib-vrf-zebra.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs', + "simple-results/result-lib-vrf-zebra-ribs.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib', + "simple-results/result-ribs-rib-nokeys.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]', + "simple-results/result-ribs-rib-ipv4-unicast.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route', + "simple-results/result-ribs-rib-route-nokey.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.1.0/24"]', + "simple-results/result-ribs-rib-route-prefix.json", + ), + # Missing entry + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.0.0/24"]', + "simple-results/result-empty.json", + ), + # Leaf reference + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.1.0/24"]/route-entry[protocol="connected"]/metric', + "simple-results/result-singleton-metric.json", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]', + "simple-results/result-intf-eth0-with-config.json", + "with-config", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]', + "simple-results/result-intf-eth0-only-config.json", + "only-config", + ), + ( + "/frr-interface:lib/interface/description", + "simple-results/result-intf-description.json", + "with-config", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]', + "simple-results/result-intf-eth0-exact.json", + "exact", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]/description', + "simple-results/result-intf-eth0-description-exact.json", + "with-config exact", + ), + # Interface state + ( + '/frr-interface:lib/interface[name="r1-eth0"]/state', + "simple-results/result-intf-state.json", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]/state/mtu', + "simple-results/result-intf-state-mtu.json", + ), + # with-defaults + ( + '/frr-interface:lib/interface[name="r1-eth0"]/frr-zebra:zebra/evpn-mh', + "simple-results/result-intf-eth0-wd-explicit.json", + "with-config exact", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]/frr-zebra:zebra/evpn-mh', + "simple-results/result-intf-eth0-wd-trim.json", + "with-config exact with-defaults trim", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]/frr-zebra:zebra/evpn-mh', + "simple-results/result-intf-eth0-wd-all.json", + "with-config exact with-defaults all", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]/frr-zebra:zebra/evpn-mh', + "simple-results/result-intf-eth0-wd-all-tag.json", + "with-config exact with-defaults all-tag", + ), + ] + + r1 = tgen.gears["r1"].net + check_kernel_32(r1, "11.11.11.11", 1, "") + do_oper_test(tgen, query_results) + + +to_gen_new_results = """ +scriptdir=~chopps/w/frr/tests/topotests/mgmt_oper +resdir=${scriptdir}/simple-results +vtysh -c 'show mgmt get-data /frr-vrf:lib' > ${resdir}/result-lib.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf' > ${resdir}/result-lib-vrf-nokey.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]' > ${resdir}/result-lib-vrf-default.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="red"]' > ${resdir}/result-lib-vrf-red.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra' > ${resdir}/result-lib-vrf-zebra.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs' > ${resdir}/result-lib-vrf-zebra-ribs.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib' > ${resdir}/result-ribs-rib-nokeys.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]' > ${resdir}/result-ribs-rib-ipv4-unicast.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route' > ${resdir}/result-ribs-rib-route-nokey.json + +vtysh -c 'show mgmt get-data /frr-interface:lib/interface[name="r1-eth0"]/state' > ${resdir}/result-intf-state.json +vtysh -c 'show mgmt get-data /frr-interface:lib/interface[name="r1-eth0"]/state/mtu' > ${resdir}/result-intf-state-mtu.json + +for f in ${resdir}/result-*; do + sed -i -e 's/"uptime": ".*"/"uptime": "rubout"/;s/"id": [0-9][0-9]*/"id": "rubout"/' $f + sed -i -e 's/"phy-address": ".*"/"phy-address": "rubout"/' $f + sed -i -e 's/"if-index": [0-9][0-9]*/"if-index": "rubout"/' $f + sed -i -e 's,"vrf": "[0-9]*","vrf": "rubout",' $f +done +""" # noqa: 501 + +# Example commands: +# show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route[prefix="1.1.0.0/24"] # noqa: E501 +# show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route[prefix="1.1.1.0/24"] # noqa: E501 diff --git a/tests/topotests/mgmt_startup/test_bigconf.py b/tests/topotests/mgmt_startup/test_bigconf.py index 4f46c8f..1c08fe6 100644 --- a/tests/topotests/mgmt_startup/test_bigconf.py +++ b/tests/topotests/mgmt_startup/test_bigconf.py @@ -21,8 +21,7 @@ from util import check_kernel, check_vtysh_up, write_big_route_conf CWD = os.path.dirname(os.path.realpath(__file__)) -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] track = Timeout(0) diff --git a/tests/topotests/mgmt_startup/test_cfgfile_var.py b/tests/topotests/mgmt_startup/test_cfgfile_var.py index 6a54f71..8203ae5 100644 --- a/tests/topotests/mgmt_startup/test_cfgfile_var.py +++ b/tests/topotests/mgmt_startup/test_cfgfile_var.py @@ -31,8 +31,7 @@ from lib.common_config import step from lib.topogen import Topogen, TopoRouter from util import check_kernel -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] @pytest.fixture(scope="module") diff --git a/tests/topotests/mgmt_startup/test_late_bigconf.py b/tests/topotests/mgmt_startup/test_late_bigconf.py index 0b5bf38..5bf3457 100644 --- a/tests/topotests/mgmt_startup/test_late_bigconf.py +++ b/tests/topotests/mgmt_startup/test_late_bigconf.py @@ -22,8 +22,7 @@ from util import check_kernel, check_vtysh_up, write_big_route_conf CWD = os.path.dirname(os.path.realpath(__file__)) -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] track = Timeout(0) ROUTE_COUNT = 2500 diff --git a/tests/topotests/mgmt_startup/test_late_uniconf.py b/tests/topotests/mgmt_startup/test_late_uniconf.py index d4e7e07..380c2eb 100644 --- a/tests/topotests/mgmt_startup/test_late_uniconf.py +++ b/tests/topotests/mgmt_startup/test_late_uniconf.py @@ -14,8 +14,7 @@ import pytest from lib.topogen import Topogen from util import _test_staticd_late_start -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] @pytest.fixture(scope="module") diff --git a/tests/topotests/mgmt_startup/test_latestart.py b/tests/topotests/mgmt_startup/test_latestart.py index 1c97b9d..c0c0121 100644 --- a/tests/topotests/mgmt_startup/test_latestart.py +++ b/tests/topotests/mgmt_startup/test_latestart.py @@ -14,8 +14,7 @@ import pytest from lib.topogen import Topogen, TopoRouter from util import _test_staticd_late_start -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] @pytest.fixture(scope="module") diff --git a/tests/topotests/mgmt_tests/test_yang_mgmt.py b/tests/topotests/mgmt_tests/test_yang_mgmt.py index 921b4e6..bf4e95b 100644 --- a/tests/topotests/mgmt_tests/test_yang_mgmt.py +++ b/tests/topotests/mgmt_tests/test_yang_mgmt.py @@ -68,7 +68,7 @@ from lib.topolog import logger from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib from lib.topojson import build_config_from_json -pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd, pytest.mark.mgmtd] # Global variables ADDR_TYPES = check_address_types() diff --git a/tests/topotests/munet/base.py b/tests/topotests/munet/base.py index 06ca4de..72b5df5 100644 --- a/tests/topotests/munet/base.py +++ b/tests/topotests/munet/base.py @@ -21,7 +21,6 @@ import subprocess import sys import tempfile import time as time_mod - from collections import defaultdict from pathlib import Path from typing import Union @@ -29,10 +28,8 @@ 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 @@ -513,9 +510,8 @@ class Commander: # pylint: disable=R0904 self.logger.debug('%s("%s") [no precmd]', method, shlex.join(cmd_list)) else: self.logger.debug( - '%s: %s %s("%s", pre_cmd: "%s" use_pty: %s kwargs: %.120s)', + '%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 "", @@ -566,7 +562,7 @@ class Commander: # pylint: disable=R0904 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', + '%s: _spawn: cmd "%s" skip_pre_cmd %s use_pty %s echo %s kwargs %s', self, cmd, skip_pre_cmd, @@ -579,7 +575,7 @@ class Commander: # pylint: disable=R0904 ) self.logger.debug( - '%s: XXX %s("%s", use_pty %s echo %s defaults: %s)', + '%s: %s("%s", use_pty %s echo %s defaults: %s)', self, "PopenSpawn" if not use_pty else "pexpect.spawn", actual_cmd, @@ -865,14 +861,18 @@ class Commander: # pylint: disable=R0904 else: o, e = await p.communicate() self.logger.debug( - "%s: cmd_p already exited status: %s", self, proc_error(p, o, e) + "%s: [cleanup_proc] proc 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) + self.logger.debug( + "%s: [cleanup_proc] 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) @@ -885,14 +885,19 @@ class Commander: # pylint: disable=R0904 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) + "%s: [cleanup_proc] exited after kill, status: %s", + self, + proc_error(p, o, e), ) except (asyncio.TimeoutError, subprocess.TimeoutExpired): - self.logger.warning("%s: SIGKILL timeout", self) + self.logger.warning("%s: [cleanup_proc] SIGKILL timeout", self) return p except Exception as error: self.logger.warning( - "%s: kill unexpected exception: %s", self, error, exc_info=True + "%s: [cleanup_proc] kill unexpected exception: %s", + self, + error, + exc_info=True, ) return p return None @@ -1206,7 +1211,7 @@ class Commander: # pylint: disable=R0904 # 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 self.is_vm and self.use_ssh and not ns_only: # pylint: disable=E1101 if isinstance(cmd, str): cmd = shlex.split(cmd) cmd = ["/usr/bin/env", f"MUNET_NODENAME={self.name}"] + cmd @@ -1226,8 +1231,14 @@ class Commander: # pylint: disable=R0904 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}" + envvars = f"MUNET_NODENAME={self.name} NODENAME={self.name}" + if hasattr(self, "rundir"): + envvars += f" RUNDIR={self.rundir}" + if hasattr(self.unet, "config_dirname") and self.unet.config_dirname: + envvars += f" CONFIGDIR={self.unet.config_dirname}" + elif "CONFIGDIR" in os.environ: + envvars += f" CONFIGDIR={os.environ['CONFIGDIR']}" + cmd = f"/usr/bin/env {envvars} {cmd}" # We need sudo b/c we are executing as the user inside the window system. sudo_path = get_exec_path_host(["sudo"]) nscmd = ( @@ -1337,6 +1348,14 @@ class Commander: # pylint: disable=R0904 "select-layout", "-t", pane_info if not tmux_target else tmux_target, + "even-horizontal", + ] + commander.cmd_status(cmd) + cmd = [ + get_exec_path_host("tmux"), + "select-layout", + "-t", + pane_info if not tmux_target else tmux_target, "tiled", ] commander.cmd_status(cmd) @@ -1915,18 +1934,19 @@ class LinuxNamespace(Commander, InterfaceMixin): assert unet is None self.uflags = uflags # - # Open file descriptors for current namespaces for later resotration. + # Open file descriptors for current namespaces for later restoration. # try: + # pidfd_open is actually present in 5.4, is this 5.8 check for another + # aspect of what the pidfd_open code is relying on, something in the + # namespace code? If not we can simply check for os.pidfd_open() being + # present as our compat module linux.py runtime patches it in if + # supported by the kernel. 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) - ): + if not kvok: # get list of namespace file descriptors before we unshare self.p_ns_fds = [] self.p_ns_fnames = [] @@ -2005,8 +2025,10 @@ class LinuxNamespace(Commander, InterfaceMixin): stdout=stdout, stderr=stderr, text=True, - start_new_session=not unet, shell=False, + # start_new_session=not unet + # preexec_fn=os.setsid if not unet else None, + preexec_fn=os.setsid, ) # The pid number returned is in the global pid namespace. For unshare_inline @@ -2345,14 +2367,14 @@ class LinuxNamespace(Commander, InterfaceMixin): and self.pid != our_pid ): self.logger.debug( - "cleanup pid on separate pid %s from proc pid %s", + "cleanup separate pid %s from namespace 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) + self.logger.debug("cleanup namespace 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 @@ -2937,7 +2959,7 @@ if True: # pylint: disable=using-constant-test ) logging.debug( - 'ShellWraper: XXX prompt "%s" will_echo %s child.echo %s', + 'ShellWraper: prompt "%s" will_echo %s child.echo %s', prompt, will_echo, spawn.echo, diff --git a/tests/topotests/munet/cli.py b/tests/topotests/munet/cli.py index f631073..133644e 100644 --- a/tests/topotests/munet/cli.py +++ b/tests/topotests/munet/cli.py @@ -325,13 +325,14 @@ def get_shcmd(unet, host, kinds, execfmt, ucmd): if not execfmt: return "" - # Do substitutions for {} in string + # Do substitutions for {} and {N} 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)): + # look for any pair of {}s but do not count escaped {{ or }} + elif len(re.findall(r"{[^}]+}", execfmt.replace("{{", "").replace("}}", ""))): if execfmt.endswith('"'): fstring = "f'''" + execfmt + "'''" else: diff --git a/tests/topotests/munet/linux.py b/tests/topotests/munet/linux.py index 417f745..519c55f 100644 --- a/tests/topotests/munet/linux.py +++ b/tests/topotests/munet/linux.py @@ -132,8 +132,17 @@ def pidfd_open(pid, flags=0): return fd +# Runtime patch if kernel supports the call. if not hasattr(os, "pidfd_open"): - os.pidfd_open = pidfd_open + try: + import platform + + kversion = [int(x) for x in platform.release().split("-")[0].split(".")] + kvok = kversion[0] > 5 or (kversion[0] == 5 and kversion[1] >= 4) + except ValueError: + kvok = False + if kvok: + os.pidfd_open = pidfd_open def setns(fd, nstype): # noqa: D402 diff --git a/tests/topotests/munet/mutest/userapi.py b/tests/topotests/munet/mutest/userapi.py index 1df8c0d..7967dd0 100644 --- a/tests/topotests/munet/mutest/userapi.py +++ b/tests/topotests/munet/mutest/userapi.py @@ -144,7 +144,6 @@ class TestCase: 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 @@ -248,7 +247,6 @@ class TestCase: 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 @@ -393,12 +391,12 @@ class TestCase: self, target: str, cmd: str, - ) -> dict: + ) -> Union[list, 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. + cmd: string to execute on the target. """ out = self.targets[target].cmd_nostatus(cmd, warn=False) self.last = out = out.rstrip() @@ -420,6 +418,7 @@ class TestCase: match: str, expect_fail: bool, flags: int, + exact_match: bool, ) -> (bool, Union[str, list]): """Execute a ``cmd`` and check result. @@ -429,6 +428,8 @@ class TestCase: 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 + exact_match: if True then ``match`` must be exactly matched somewhere + in the output of ``cmd`` using ``str.find()``. Returns: (success, matches): if the match fails then "matches" will be None, @@ -436,6 +437,17 @@ class TestCase: ``matches`` otherwise group(0) (i.e., the matching text). """ out = self._command(target, cmd) + if exact_match: + if match not in out: + success = expect_fail + ret = None + else: + success = not expect_fail + ret = match + level = logging.DEBUG if success else logging.WARNING + self.olog.log(level, "exactly matched:%s:", ret) + return success, ret + search = re.search(match, out, flags) self.last_m = search if search is None: @@ -455,17 +467,19 @@ class TestCase: self, target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, dict], expect_fail: bool, - ) -> Union[str, dict]: + exact_match: bool, + ) -> (bool, Union[list, 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``. + match: A json ``str``, object (``dict``), or array (``list``) to + compare against the json output from ``cmd``. expect_fail: if True then succeed when the json doesn't match. + exact_match: if True then the json must exactly match. """ js = self._command_json(target, cmd) try: @@ -476,7 +490,27 @@ class TestCase: "JSON load failed. Check match value is in JSON format: %s", error ) - if json_diff := json_cmp(expect, js): + if exact_match: + deep_diff = json_cmp(expect, js) + # Convert DeepDiff completely into dicts or lists at all levels + json_diff = json.loads(deep_diff.to_json()) + else: + deep_diff = json_cmp(expect, js, ignore_order=True) + # Convert DeepDiff completely into dicts or lists at all levels + json_diff = json.loads(deep_diff.to_json()) + # Remove new fields in json object from diff + if json_diff.get("dictionary_item_added") is not None: + del json_diff["dictionary_item_added"] + # Remove new json objects in json array from diff + if (new_items := json_diff.get("iterable_item_added")) is not None: + new_item_paths = list(new_items.keys()) + for path in new_item_paths: + if type(new_items[path]) is dict: + del new_items[path] + if len(new_items) == 0: + del json_diff["iterable_item_added"] + + if json_diff: success = expect_fail if not success: self.logf("JSON DIFF:%s:" % json_diff) @@ -489,14 +523,24 @@ class TestCase: self, target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, 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.""" + exact_match: bool, + ) -> Union[str, list, dict]: + """Execute a command repeatedly waiting for result until timeout. + + ``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, a json array, + or a ``str`` which parses into a json object. Likewise, the ``cmd`` output + is parsed into a json object or array and then a comparison is done between + the two json objects or arrays. + """ startt = time.time() endt = startt + timeout @@ -504,10 +548,12 @@ class TestCase: ret = None while not success and time.time() < endt: if is_json: - success, ret = self._match_command_json(target, cmd, match, expect_fail) + success, ret = self._match_command_json( + target, cmd, match, expect_fail, exact_match + ) else: success, ret = self._match_command( - target, cmd, match, expect_fail, flags + target, cmd, match, expect_fail, flags, exact_match ) if not success: time.sleep(interval) @@ -626,7 +672,7 @@ class TestCase: ) return self._command(target, cmd) - def step_json(self, target: str, cmd: str) -> dict: + def step_json(self, target: str, cmd: str) -> Union[list, dict]: """See :py:func:`~munet.mutest.userapi.step_json`. :meta private: @@ -649,13 +695,14 @@ class TestCase: desc: str = "", expect_fail: bool = False, flags: int = re.DOTALL, + exact_match: bool = False, ) -> (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", + "#%s.%s:%s:MATCH_STEP:%s:%s:%s:%s:%s:%s:%s", self.tag, self.steps + 1, self.info.path, @@ -665,8 +712,11 @@ class TestCase: desc, expect_fail, flags, + exact_match, + ) + success, ret = self._match_command( + target, cmd, match, expect_fail, flags, exact_match ) - success, ret = self._match_command(target, cmd, match, expect_fail, flags) if desc: self.__post_result(target, success, desc) return success, ret @@ -684,16 +734,17 @@ class TestCase: self, target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, dict], desc: str = "", expect_fail: bool = False, - ) -> (bool, Union[str, dict]): + exact_match: bool = False, + ) -> (bool, Union[list, 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", + "#%s.%s:%s:MATCH_STEP_JSON:%s:%s:%s:%s:%s:%s", self.tag, self.steps + 1, self.info.path, @@ -702,8 +753,11 @@ class TestCase: match, desc, expect_fail, + exact_match, + ) + success, ret = self._match_command_json( + target, cmd, match, expect_fail, exact_match ) - success, ret = self._match_command_json(target, cmd, match, expect_fail) if desc: self.__post_result(target, success, desc) return success, ret @@ -718,6 +772,7 @@ class TestCase: interval=0.5, expect_fail: bool = False, flags: int = re.DOTALL, + exact_match: bool = False, ) -> (bool, Union[str, list]): """See :py:func:`~munet.mutest.userapi.wait_step`. @@ -726,7 +781,7 @@ class TestCase: 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", + "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s:%s:%s", self.tag, self.steps + 1, self.info.path, @@ -738,9 +793,18 @@ class TestCase: desc, expect_fail, flags, + exact_match, ) success, ret = self._wait( - target, cmd, match, False, timeout, interval, expect_fail, flags + target, + cmd, + match, + False, + timeout, + interval, + expect_fail, + flags, + exact_match, ) if desc: self.__post_result(target, success, desc) @@ -750,12 +814,13 @@ class TestCase: self, target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, dict], desc: str = "", timeout=10, interval=None, expect_fail: bool = False, - ) -> (bool, Union[str, dict]): + exact_match: bool = False, + ) -> (bool, Union[list, dict]): """See :py:func:`~munet.mutest.userapi.wait_step_json`. :meta private: @@ -763,7 +828,7 @@ class TestCase: 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.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s:%s", self.tag, self.steps + 1, self.info.path, @@ -774,9 +839,10 @@ class TestCase: interval, desc, expect_fail, + exact_match, ) success, ret = self._wait( - target, cmd, match, True, timeout, interval, expect_fail, 0 + target, cmd, match, True, timeout, interval, expect_fail, 0, exact_match ) if desc: self.__post_result(target, success, desc) @@ -864,15 +930,15 @@ def step(target: str, cmd: str) -> str: 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. +def step_json(target: str, cmd: str) -> Union[list, dict]: + """Execute a json ``cmd`` on a ``target`` and return the json object or array. 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. + Returns the json object or array after parsing the ``cmd`` output. If json parse fails, a warning is logged and an empty ``dict`` is used. """ @@ -904,6 +970,7 @@ def match_step( desc: str = "", expect_fail: bool = False, flags: int = re.DOTALL, + exact_match: bool = False, ) -> (bool, Union[str, list]): """Execute a ``cmd`` on a ``target`` check result. @@ -922,44 +989,53 @@ def match_step( 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 + exact_match: if True then ``match`` must be exactly matched somewhere + in the output of ``cmd`` using ``str.find()``. 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) + return TestCase.g_tc.match_step( + target, cmd, match, desc, expect_fail, flags, exact_match + ) def match_step_json( target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, dict], desc: str = "", expect_fail: bool = False, -) -> (bool, Union[str, dict]): + exact_match: bool = False, +) -> (bool, Union[list, dict]): """Execute a ``cmd`` on a ``target`` check result. - Execute ``cmd`` on ``target`` and check if the json object in ``match`` + Execute ``cmd`` on ``target`` and check if the json object or array 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``. + match: A json ``str``, object (``dict``), or array (``list``) 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. + exact_match: if True then the json must exactly 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``. + second value is a ``dict`` of the diff if there is a difference found in + the json compare, otherwise the value is the json object (``dict``) or + array (``list``) 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) + return TestCase.g_tc.match_step_json( + target, cmd, match, desc, expect_fail, exact_match + ) def wait_step( @@ -971,6 +1047,7 @@ def wait_step( interval: float = 0.5, expect_fail: bool = False, flags: int = re.DOTALL, + exact_match: bool = False, ) -> (bool, Union[str, list]): """Execute a ``cmd`` on a ``target`` repeatedly, looking for a result. @@ -991,6 +1068,8 @@ def wait_step( 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 + exact_match: if True then ``match`` must be exactly matched somewhere + in the output of ``cmd`` using ``str.find()``. Returns: Returns a 2-tuple. The first value is a bool indicating ``success``. @@ -998,37 +1077,31 @@ def wait_step( 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 + target, cmd, match, desc, timeout, interval, expect_fail, flags, exact_match ) def wait_step_json( target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, dict], desc: str = "", timeout=10, interval=None, expect_fail: bool = False, -) -> (bool, Union[str, dict]): + exact_match: bool = False, +) -> (bool, Union[list, 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``. + match: A json object, json array, or str representation of json 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). @@ -1037,17 +1110,18 @@ def wait_step_json( 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. + exact_match: if True then the json must exactly 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. + The second value is a ``dict`` of the diff if there is a difference + found in the json compare, otherwise the value is a json object (``dict``) + or array (``list``) 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 + target, cmd, match, desc, timeout, interval, expect_fail, exact_match ) diff --git a/tests/topotests/munet/native.py b/tests/topotests/munet/native.py index fecf709..4fbbb85 100644 --- a/tests/topotests/munet/native.py +++ b/tests/topotests/munet/native.py @@ -20,6 +20,8 @@ import socket import subprocess import time +from pathlib import Path + from . import cli from .base import BaseMunet from .base import Bridge @@ -38,6 +40,7 @@ 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 +from .watchlog import WatchLog class L3ContainerNotRunningError(MunetError): @@ -455,13 +458,14 @@ class NodeMixin: bps = self.unet.cfgopt.getoption("--gdb-breakpoints", "").split(",") for bp in bps: - gdbcmd += f" '-ex=b {bp}'" + if bp: + gdbcmd += f" '-ex=b {bp}'" - cmds = self.config.get("gdb-run-cmd", []) + cmds = self.config.get("gdb-run-cmds", []) for cmd in cmds: gdbcmd += f" '-ex={cmd}'" - self.run_in_window(gdbcmd) + self.run_in_window(gdbcmd, ns_only=True) elif should_gdb and use_emacs: gdbcmd = gdbcmd.replace("gdb ", "gdb -i=mi ") ecbin = self.get_exec_path("emacsclient") @@ -664,6 +668,7 @@ class L3NodeMixin(NodeMixin): self.phycount = 0 self.phy_odrivers = {} self.tapmacs = {} + self.watched_logs = {} self.intf_tc_count = 0 @@ -723,6 +728,26 @@ ff02::2\tip6-allrouters if hasattr(self, "bind_mount"): self.bind_mount(hosts_file, "/etc/hosts") + def add_watch_log(self, path, watchfor_re=None): + """Add a WatchLog to this nodes watched logs. + + Args: + path: If relative is relative to the nodes ``rundir`` + watchfor_re: Regular expression to watch the log for and raise an exception + if found. + + Return: + The watching task if request or None otherwise. + """ + path = Path(path) + if not path.is_absolute(): + path = self.rundir.joinpath(path) + + wl = WatchLog(path) + self.watched_logs[wl.path] = wl + task = wl.raise_if_match_task(watchfor_re) if watchfor_re else None + return task + async def console( self, concmd, @@ -938,8 +963,32 @@ ff02::2\tip6-allrouters 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}") + + # See if this interace is missing and needs to be fixed + rc, o, _ = self.unet.rootcmd.cmd_status("ip -o link show") + m = re.search(rf"\d+:\s+(\S+):.*altname {re.escape(hname)}\W", o) + if m: + # need to rename + dname = m.group(1) + self.logger.info("Fixing misnamed %s to %s", dname, hname) + self.unet.rootcmd.cmd_status( + f"ip link property del dev {dname} altname {hname}" + ) + self.unet.rootcmd.cmd_status(f"ip link set {dname} name {hname}") + + rc, o, _ = self.unet.rootcmd.cmd_status("ip -o link show") + m = re.search(rf"\d+:\s+{re.escape(hname)}:.*", o) + if m: + self.unet.rootcmd.cmd_nostatus(f"ip link set {hname} down ") + self.unet.rootcmd.cmd_raises(f"ip link set {hname} netns {self.pid}") + # Wait for interface to show up in namespace + for retry in range(0, 10): + rc, o, _ = self.cmd_status(f"ip -o link show {hname}") + if not rc: + if re.search(rf"\d+: {re.escape(hname)}:.*", o): + break + if retry > 0: + await asyncio.sleep(1) self.cmd_raises(f"ip link set {hname} name {lname}") if mtu: self.cmd_raises(f"ip link set {lname} mtu {mtu}") @@ -949,7 +998,12 @@ ff02::2\tip6-allrouters 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") + self.cmd_status(f"ip link set netns 1 dev {hname}") + # The above is failing sometimes and not sure why + # logging.error( + # "XXX after setns %s", + # self.unet.rootcmd.cmd_nostatus(f"ip link show {hname}"), + # ) del self.host_intfs[hname] async def add_phy_intf(self, devaddr, lname): @@ -1019,12 +1073,13 @@ ff02::2\tip6-allrouters "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" + f"echo {devaddr} | timeout 10 tee /sys/bus/pci/drivers/{driver}/unbind" ) # Add the device vendor and device id to vfio-pci in case it's the first time @@ -1035,7 +1090,14 @@ ff02::2\tip6-allrouters 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}"): + for retry in range(0, 10): + if self.unet.rootcmd.path_exists( + f"/sys/bus/pci/drivers/vfio-pci/{devaddr}" + ): + break + if retry > 0: + await asyncio.sleep(1) + # 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( @@ -1066,7 +1128,7 @@ ff02::2\tip6-allrouters "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" + f"echo {devaddr} | timeout 10 tee /sys/bus/pci/drivers/vfio-pci/unbind" ) self.logger.info("Binding physical PCI device %s to driver %s", devaddr, driver) @@ -1085,13 +1147,13 @@ ff02::2\tip6-allrouters 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() + # remove any hostintf interfaces, needs to come after normal exits + for devaddr in list(self.phy_intfs): + await self.rem_phy_intf(devaddr) + class L3NamespaceNode(L3NodeMixin, LinuxNamespace): """A namespace L3 node.""" @@ -1123,6 +1185,7 @@ class L3ContainerNode(L3NodeMixin, LinuxNamespace): assert self.container_image self.cmd_p = None + self.cmd_pid = None self.__base_cmd = [] self.__base_cmd_pty = [] @@ -1393,7 +1456,13 @@ class L3ContainerNode(L3NodeMixin, LinuxNamespace): start_new_session=True, # keeps main tty signals away from podman ) - self.logger.debug("%s: async_popen => %s", self, self.cmd_p.pid) + # 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)", self, self.cmd_p.pid, self.cmd_pid + ) self.pytest_hook_run_cmd(stdout, stderr) @@ -1542,6 +1611,7 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): """Create a Container Node.""" self.cont_exec_paths = {} self.launch_p = None + self.launch_pid = None self.qemu_config = config["qemu"] self.extra_mounts = [] assert self.qemu_config @@ -1968,8 +2038,9 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): 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") + # This is already mounted now + # 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 @@ -2261,25 +2332,29 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): 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( + self.launch_p = await self.async_popen_nsonly( 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 + # Don't want Keybaord interrupt etc to pass to child. + # start_new_session=True, + preexec_fn=os.setsid, ) + if self.nsenter_fork: + self.launch_pid = await self.get_proc_child_pid(self.launch_p) + 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) + self.logger.debug( + "%s: popen => %s (%s)", self, self.launch_p.pid, self.launch_pid + ) confiles = ["_console"] if use_cmdcon: @@ -2307,10 +2382,10 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): # the monitor output has super annoying ANSI escapes in it output = self.monrepl.cmd_nostatus("info status") - self.logger.info("VM status: %s", output) + self.logger.debug("VM status: %s", output) output = self.monrepl.cmd_nostatus("info kvm") - self.logger.info("KVM status: %s", output) + self.logger.debug("KVM status: %s", output) # # Set thread affinity @@ -2348,11 +2423,6 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): "%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 @@ -2372,7 +2442,7 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): # Need to cleanup early b/c it is running on the VM if self.cmd_p: - await self.async_cleanup_proc(self.cmd_p) + await self.async_cleanup_proc(self.cmd_p, self.cmd_pid) self.cmd_p = None try: @@ -2388,9 +2458,9 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): if not self.launch_p: self.logger.warning("async_delete: qemu is not running") else: - await self.cleanup_qemu() + await self.async_cleanup_proc(self.launch_p, self.launch_pid) except Exception as error: - self.logger.warning("%s: failued to cleanup qemu process: %s", self, error) + self.logger.warning("%s: failed to cleanup qemu process: %s", self, error) await super()._async_delete() @@ -2814,6 +2884,8 @@ ff02::2\tip6-allrouters logging.debug("Launching nodes") await asyncio.gather(*[x.launch() for x in launch_nodes]) + logging.debug("Launched nodes -- Queueing Waits") + # Watch for launched processes to exit for node in launch_nodes: task = asyncio.create_task( @@ -2822,17 +2894,23 @@ ff02::2\tip6-allrouters task.add_done_callback(node.launch_completed) tasks.append(task) + logging.debug("Wait complete queued, running cmd") + 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]) + logging.debug("Ran cmds -- Queueing Waits") + # 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) + logging.debug("Wait complete queued, waiting for ready") + # Wait for nodes to be ready if ready_nodes: @@ -2853,6 +2931,8 @@ ff02::2\tip6-allrouters raise asyncio.TimeoutError() logging.debug("All nodes ready") + logging.debug("All done returning tasks: %s", tasks) + return tasks async def _async_delete(self): diff --git a/tests/topotests/munet/testing/fixtures.py b/tests/topotests/munet/testing/fixtures.py index 25039df..3c6d946 100644 --- a/tests/topotests/munet/testing/fixtures.py +++ b/tests/topotests/munet/testing/fixtures.py @@ -95,7 +95,7 @@ def _push_log_handler(desc, 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") + fmt = logging.Formatter("%(asctime)s %(levelname)5s: %(name)s: %(message)s") handler.setFormatter(fmt) root_logger.addHandler(handler) return handler diff --git a/tests/topotests/munet/testing/hooks.py b/tests/topotests/munet/testing/hooks.py index 9b6a49a..985eef9 100644 --- a/tests/topotests/munet/testing/hooks.py +++ b/tests/topotests/munet/testing/hooks.py @@ -196,10 +196,10 @@ def pytest_runtest_makereport(item, call): if error: item.skip_more_pause = True - # we can't asyncio.run() (which pause does) if we are unhsare_inline + # we can't asyncio.run() (which pause does) if we are not 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: + if BaseMunet.g_unet and 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 diff --git a/tests/topotests/munet/watchlog.py b/tests/topotests/munet/watchlog.py new file mode 100644 index 0000000..27bc325 --- /dev/null +++ b/tests/topotests/munet/watchlog.py @@ -0,0 +1,170 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# August 21 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +"""A module supporting an object for watching a logfile.""" +import asyncio +import logging +import re + +from pathlib import Path + + +class MatchFoundError(Exception): + """An error raised when a match is not found.""" + def __init__(self, watchlog, match): + self.watchlog = watchlog + self.match = match + super().__init__(watchlog, match) + + +class WatchLog: + """An object for watching a logfile.""" + + def __init__(self, path, encoding="utf-8"): + """Watch a logfile. + + Args: + path: that path of the logfile to watch + encoding: the encoding of the logfile + """ + # Immutable + self.path = Path(path) + self.encoding = encoding + + # Mutable + self.content = "" + self.last_snap_mark = 0 + self.last_user_mark = 0 + self.stat = None + + if self.path.exists(): + self.snapshot() + + def _stat_snapshot(self): + ostat = self.stat + + if not self.path.exists(): + self.stat = None + return ostat is not None + + stat = self.path.stat() + self.stat = stat + + if ostat is None: + return True + + return ( + stat.st_mtime_ns != ostat.st_mtime_ns + or stat.st_ctime_ns != ostat.st_ctime_ns + or stat.st_ino != ostat.st_ino + or stat.st_size != ostat.st_size + ) + + def reset(self): + self.content = "" + self.last_user_mark = 0 + self.last_snap_mark = 0 + + def update_content(self): + ostat = self.stat + osize = ostat.st_size if ostat else 0 + oino = ostat.st_ino if ostat else -1 + if not self._stat_snapshot(): + logging.debug("XXX logfile %s no stat change", self.path) + return "" + + nino = self.stat.st_ino + # If the inode changed and we had content previously warn + if oino != -1 and oino != nino and self.content: + logging.warning( + "logfile %s replaced (new inode) resetting content", self.path + ) + self.reset() + osize = 0 + + nsize = self.stat.st_size + if osize > nsize: + logging.warning("logfile %s shrunk resetting content", self.path) + self.reset() + osize = 0 + + if osize == nsize: + logging.debug( + "XXX watchlog: %s no update, osize == nsize == %s", self.path, osize + ) + return "" + + # Read non-blocking + with open(self.path, "r", encoding=self.encoding) as f: + if osize: + f.seek(osize) + logging.debug( + "XXX watchlog: %s reading new content from %s to %s", + self.path, + osize, + nsize, + ) + newcontent = f.read(nsize - osize) + + self.content += newcontent + return newcontent + + def raise_if_match_task(self, match): + """Start an async task that searches for a match. + + This doesn't work well with pytest as the task must be awaited for the exception + to propagate. + """ + + async def scan_for_match(wl, regex): + while True: + logging.debug("watchlog: %s scan for updating content", wl.path) + wl.update_content() + if m := regex.search(wl.content): + logging.error( + "XXX watchlog: %s regexp FOUND raising exception!", wl.path + ) + raise MatchFoundError(wl, m) + await asyncio.sleep(2) + + aw = scan_for_match(self, re.compile(match)) + return asyncio.create_task(aw) + + def from_mark(self, mark=None): + """Return the file content starting from ``mark``. + + If ``mark`` is None then return content since last ``set_mark`` was called. + + Args: + mark: the mark in the content to return file content from. + + Return: + returns the content between ``mark`` and the end of content. + """ + return self.content[mark:] + + def set_mark(self): + """Set a mark for later use.""" + last_mark = self.last_user_mark + self.last_user_mark = len(self.content) + return last_mark + + def snapshot(self): + """Update the file content and return new text. + + Returns any new text added since the last snapshot, + also updates the snapshot mark. + + Return: + Newly added text. + """ + # Update the content which may reset marks + self.update_content() + + last_mark = self.last_snap_mark + self.last_snap_mark = len(self.content) + return self.content[last_mark:] diff --git a/tests/topotests/nb_config/r1/frr.conf b/tests/topotests/nb_config/r1/frr.conf new file mode 100644 index 0000000..677ec0b --- /dev/null +++ b/tests/topotests/nb_config/r1/frr.conf @@ -0,0 +1,6 @@ +log timestamp precision 6 +log file frr.log + +interface r1-eth0 + ip address 1.1.1.1/24 +exit diff --git a/tests/topotests/nb_config/test_nb_config.py b/tests/topotests/nb_config/test_nb_config.py new file mode 100644 index 0000000..f699a4e --- /dev/null +++ b/tests/topotests/nb_config/test_nb_config.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# February 24 2024, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2024, LabN Consulting, L.L.C. +# +""" +Test Northbound Config Operations +""" +import json +import os + +import pytest +from lib.topogen import Topogen +from lib.topotest import json_cmp + +pytestmark = [pytest.mark.mgmtd] + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = { + "s1": ("r1",) + } + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_access_list_config_ordering(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + output = r1.vtysh_multicmd([ + "conf t", + "access-list test seq 1 permit host 10.0.0.1"]) + output = r1.vtysh_cmd("show ip access-list test json") + got = json.loads(output) + expected = json.loads('{"ZEBRA":{"test":{"type":"Standard", "addressFamily":"IPv4", "rules":[{"sequenceNumber":1, "filterType":"permit", "address":"10.0.0.1", "mask":"0.0.0.0"}]}}}') + result = json_cmp(got, expected) + assert result is None + + # + # If the northbound mis-orders the create/delete then this test fails. + # https://github.com/FRRouting/frr/pull/15423/commits/38b85e0c2bc555b8827dbd2cb6515b6febf548b4 + # + output = r1.vtysh_multicmd([ + "conf t", + "access-list test seq 1 permit 10.0.0.0/8"]) + output = r1.vtysh_cmd("show ip access-list test json") + got = json.loads(output) + expected = json.loads('{"ZEBRA":{"test":{"type":"Zebra", "addressFamily":"IPv4", "rules":[{"sequenceNumber":1, "filterType":"permit", "prefix":"10.0.0.0/8", "exact-match":false}]}}}') + result = json_cmp(got, expected) + assert result is None diff --git a/tests/topotests/nhrp_topo/test_nhrp_topo.py b/tests/topotests/nhrp_topo/test_nhrp_topo.py index 78b82ed..26115de 100644 --- a/tests/topotests/nhrp_topo/test_nhrp_topo.py +++ b/tests/topotests/nhrp_topo/test_nhrp_topo.py @@ -182,6 +182,27 @@ def test_protocols_convergence(): assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg + # check that the NOARP flag is removed from rX-gre0 interfaces + for rname, router in router_list.items(): + if rname == "r3": + continue + + expected = { + "{}-gre0".format(rname): { + "flags": "<UP,RUNNING>", + } + } + test_func = partial( + topotest.router_json_cmp, + router, + "show interface {}-gre0 json".format(rname), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + + assertmsg = '"{}-gre0 interface flags incorrect'.format(router.name) + assert result is None, assertmsg + for rname, router in router_list.items(): if rname == "r3": continue diff --git a/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py b/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py index ec15ff9..2eaccb8 100644 --- a/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py +++ b/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py @@ -110,15 +110,18 @@ def test_wait_protocol_convergence(): def expect_neighbor_full(router, neighbor): "Wait until OSPFv3 neighborship is full" - logger.info("waiting for OSPFv3 router '{}' neighborship with '{}'".format(router, neighbor)) + logger.info( + "waiting for OSPFv3 router '{}' neighborship with '{}'".format( + router, neighbor + ) + ) test_func = partial( topotest.router_json_cmp, tgen.gears[router], "show ipv6 ospf6 neighbor json", {"neighbors": [{"neighborId": neighbor, "state": "Full"}]}, ) - _, result = topotest.run_and_expect(test_func, None, - count=130, wait=1) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) assertmsg = '"{}" convergence failure'.format(router) assert result is None, assertmsg @@ -143,6 +146,7 @@ def test_wait_protocol_convergence(): expect_neighbor_full("r8", "10.254.254.5") expect_neighbor_full("r9", "10.254.254.5") + def test_ecmp_inter_area(): "Test whether OSPFv3 ECMP nexthops are properly updated for inter-area routes after link down" tgen = get_topogen() @@ -156,22 +160,28 @@ def test_ecmp_inter_area(): def expect_num_nexthops(router, expected_num_nexthops, count): "Wait until number of nexthops for routes matches expectation" - logger.info("waiting for OSPFv3 router '{}' nexthops {}".format(router, expected_num_nexthops)) + logger.info( + "waiting for OSPFv3 router '{}' nexthops {}".format( + router, expected_num_nexthops + ) + ) test_func = partial(num_nexthops, router) - _, result = topotest.run_and_expect(test_func, expected_num_nexthops, - count=count, wait=3) - assert result == expected_num_nexthops, \ - "'{}' wrong number of route nexthops".format(router) + _, result = topotest.run_and_expect( + test_func, expected_num_nexthops, count=count, wait=3 + ) + assert ( + result == expected_num_nexthops + ), "'{}' wrong number of route nexthops".format(router) # Check nexthops pre link-down - expect_num_nexthops("r1", [1, 1, 1, 3, 3, 3, 3, 3], 4) + expect_num_nexthops("r1", [1, 1, 1, 3, 3, 3, 3, 3, 3, 3], 4) logger.info("triggering R2-R4 link down") tgen.gears["r2"].run("ip link set r2-eth1 down") - #tgen.mininet_cli() + # tgen.mininet_cli() # Check nexthops post link-down - expect_num_nexthops("r1", [1, 1, 1, 2, 2, 2, 2, 2], 8) + expect_num_nexthops("r1", [1, 1, 1, 2, 2, 2, 2, 2, 2, 2], 8) def teardown_module(_mod): diff --git a/tests/topotests/ospf6_point_to_multipoint/README.md b/tests/topotests/ospf6_point_to_multipoint/README.md new file mode 100644 index 0000000..59c9837 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/README.md @@ -0,0 +1,137 @@ +# OSPFv3 (IPv6) Topology Test (point-to-multipoint) + +## Topology + -----\ + SW1 - Stub Net 1 SW2 - Stub Net 2 \ + fc00:1:1:1::/64 fc00:2:2:2::/64 \ + \___________________/ \___________________/ | + | | | + | | | + | ::1 | ::2 | + +---------+---------+ +---------+---------+ | + | R1 | | R2 | | + | FRRouting | | FRRouting | | + | Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | | + +---------+---------+ +---------+---------+ | + | ::1 | ::2 \ + \______ ___________/ OSPFv3 + \ / Area 0.0.0.0 + \ / / + ~~~~~~~~~~~~~~~~~~ | + ~~ SW5 ~~ | + ~~ Switch ~~ | + ~~ fc00:A:A:A::/64 ~~ | + ~~~~~~~~~~~~~~~~~~ | + | /---- | + | ::3 | SW3 - Stub Net 3 | + +---------+---------+ /-+ fc00:3:3:3::/64 | + | R3 | / | / + | FRRouting +--/ \---- / + | Rtr-ID: 10.0.0.3 | ::3 ___________/ + +---------+---------+ \ + | ::3 \ + | \ + ~~~~~~~~~~~~~~~~~~ | + ~~ SW6 ~~ | + ~~ Switch ~~ | + ~~ fc00:B:B:B::/64 ~~ \ + ~~~~~~~~~~~~~~~~~~ OSPFv3 + | Area 0.0.0.1 + | ::4 / + +---------+---------+ /---- | + | R4 | | SW4 - Stub Net 4 | + | FRRouting +------+ fc00:4:4:4::/64 | + | Rtr-ID: 10.0.0.4 | ::4 | / + +-------------------+ \---- / + -----/ + +## FRR Configuration + +Full config as used is in r1 / r2 / r3 / r4 / r5 subdirectories + +Simplified `R1` config (R1 is similar) + + hostname r1 + ! + interface r1-stubnet + ipv6 address fc00:1:1:1::1/64 + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.0 + ! + interface r1-sw5 + ipv6 address fc00:a:a:a::1/64 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.0 + ! + router ospf6 + router-id 10.0.0.1 + log-adjacency-changes detail + redistribute static + ! + ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234 + +Simplified `R3` config + + hostname r3 + ! + interface r3-stubnet + ipv6 address fc00:3:3:3::3/64 + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.0 + ! + interface r3-sw5 + ipv6 address fc00:a:a:a::3/64 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.0 + ipv6 ospf6 p2p-p2mp connected-prefixes include + ! + interface r3-sw6 + ipv6 address fc00:b:b:b::3/64 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.1 + ipv6 ospf6 p2p-p2mp connected-prefixes include + ! + router ospf6 + router-id 10.0.0.3 + log-adjacency-changes detail + redistribute static + ! + ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234 + +## Tests executed + +### Check if FRR is running + +Test is executed by running + + vtysh -c "show logging" | grep "Logging configuration for" + +on each FRR router. This should return the logging information for all daemons registered +to Zebra and the list of running daemons is compared to the daemons started for this test (`zebra` and `ospf6d`) + +### Check if OSPFv3 to converge + +OSPFv3 is expected to converge on each view within 60s total time. Convergence is verified by executing (on each node) + + vtysh -c "show ipv6 ospf neigh" + +and checking for "Full" neighbor status in the output. An additional 15 seconds after the full converge is waited for +routes to populate before the following routing table checks are executed + +### Check OSPFv3 Routing Tables + +Routing table is verified by running + + vtysh -c "show ipv6 route" + +on each node and comparing the result to the stored example config (see `show_ipv6_route.ref` in r1 / r2 / r3 / r4 directories). +Link-Local addresses are masked out before the compare. + +### Check Linux Kernel Routing Table + +Linux Kernel IPv6 Routing table is verified on each FRR node with + + ip -6 route + +Tables are compared with reference routing table (see `ip_6_address.ref` in r1 / r2 / r3 / r4 directories). +Link-Local addresses are translated after getting collected on each node with interface name to make them consistent diff --git a/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.nhg.ref b/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.nhg.ref new file mode 100644 index 0000000..203f2de --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.nhg.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium +fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::2 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::3 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::3 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::4 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.ref b/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.ref new file mode 100644 index 0000000..b77c996 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium +fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium +fc00:2222:2222:2222::/64 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::2 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::3 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::3 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::4 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r1/ospf6d.conf b/tests/topotests/ospf6_point_to_multipoint/r1/ospf6d.conf new file mode 100644 index 0000000..79a9dca --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r1/ospf6d.conf @@ -0,0 +1,30 @@ +hostname r1 +log file ospf6d.log +! +! debug ospf6 message all +! debug ospf6 lsa unknown +! debug ospf6 zebra +! debug ospf6 interface +! debug ospf6 neighbor +! debug ospf6 route table +! debug ospf6 flooding +! +interface r1-sw5 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 p2p-p2mp connected-prefixes include +! +interface r1-stubnet + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.0 +! +router ospf6 + ospf6 router-id 10.0.0.1 + log-adjacency-changes detail + redistribute static +! +line vty + exec-timeout 0 0 +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r1/show_ipv6_route.ref b/tests/topotests/ospf6_point_to_multipoint/r1/show_ipv6_route.ref new file mode 100644 index 0000000..911b3e4 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r1/show_ipv6_route.ref @@ -0,0 +1,13 @@ +O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet, weight 1, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::2/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::4/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_point_to_multipoint/r1/zebra.conf b/tests/topotests/ospf6_point_to_multipoint/r1/zebra.conf new file mode 100644 index 0000000..3a7db9f --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r1/zebra.conf @@ -0,0 +1,20 @@ +! +hostname r1 +log file zebra.log +! +! debug zebra events +! debug zebra rib +! +interface r1-stubnet + ipv6 address fc00:1:1:1::1/64 +! +interface r1-sw5 + ipv6 address fc00:a:a:a::1/64 +! +interface lo +! +ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234 +! +! +line vty +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.nhg.ref b/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.nhg.ref new file mode 100644 index 0000000..d4534b1 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.nhg.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fc00:2:2:2::1234 dev r2-stubnet proto XXXX metric 20 pref medium +fc00:2:2:2::/64 dev r2-stubnet proto XXXX metric 256 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r2-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::1 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::3 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::3 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::4 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.ref b/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.ref new file mode 100644 index 0000000..330b696 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 via fc00:2:2:2::1234 dev r2-stubnet proto XXXX metric 20 pref medium +fc00:2:2:2::/64 dev r2-stubnet proto XXXX metric 256 pref medium +fc00:3333:3333:3333::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r2-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::1 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::3 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::3 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::4 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r2/ospf6d.conf b/tests/topotests/ospf6_point_to_multipoint/r2/ospf6d.conf new file mode 100644 index 0000000..751d8d8 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r2/ospf6d.conf @@ -0,0 +1,30 @@ +hostname r2 +log file ospf6d.log +! +! debug ospf6 message all +! debug ospf6 lsa unknown +! debug ospf6 zebra +! debug ospf6 interface +! debug ospf6 neighbor +! debug ospf6 route table +! debug ospf6 flooding +! +interface r2-sw5 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 p2p-p2mp connected-prefixes include +! +interface r2-stubnet + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.0 +! +router ospf6 + ospf6 router-id 10.0.0.2 + log-adjacency-changes detail + redistribute static +! +line vty + exec-timeout 0 0 +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r2/show_ipv6_route.ref b/tests/topotests/ospf6_point_to_multipoint/r2/show_ipv6_route.ref new file mode 100644 index 0000000..2eff029 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r2/show_ipv6_route.ref @@ -0,0 +1,13 @@ +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet, weight 1, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::1/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::4/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_point_to_multipoint/r2/zebra.conf b/tests/topotests/ospf6_point_to_multipoint/r2/zebra.conf new file mode 100644 index 0000000..5571dc9 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r2/zebra.conf @@ -0,0 +1,20 @@ +! +hostname r2 +log file zebra.log +! +! debug zebra events +! debug zebra rib +! +interface r2-stubnet + ipv6 address fc00:2:2:2::2/64 +! +interface r2-sw5 + ipv6 address fc00:a:a:a::2/64 +! +interface lo +! +ipv6 route fc00:2222:2222:2222::/64 fc00:2:2:2::1234 +! +! +line vty +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.nhg.ref b/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.nhg.ref new file mode 100644 index 0000000..903c1d9 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.nhg.ref @@ -0,0 +1,13 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fc00:3:3:3::1234 dev r3-stubnet proto XXXX metric 20 pref medium +fc00:3:3:3::/64 dev r3-stubnet proto XXXX metric 256 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r3-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::1 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::2 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 dev r3-sw6 proto XXXX metric 256 pref medium +fc00:b:b:b::4 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.ref b/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.ref new file mode 100644 index 0000000..3dbcf36 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.ref @@ -0,0 +1,13 @@ +fc00:1111:1111:1111::/64 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 via fc00:3:3:3::1234 dev r3-stubnet proto XXXX metric 20 pref medium +fc00:3:3:3::/64 dev r3-stubnet proto XXXX metric 256 pref medium +fc00:4444:4444:4444::/64 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r3-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::1 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::2 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 dev r3-sw6 proto XXXX metric 256 pref medium +fc00:b:b:b::4 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r3/ospf6d.conf b/tests/topotests/ospf6_point_to_multipoint/r3/ospf6d.conf new file mode 100644 index 0000000..74e8ddf --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r3/ospf6d.conf @@ -0,0 +1,37 @@ +hostname r3 +log file ospf6d.log +! +! debug ospf6 message all +! debug ospf6 lsa unknown +! debug ospf6 zebra +! debug ospf6 interface +! debug ospf6 neighbor +! debug ospf6 route table +! debug ospf6 flooding +! +interface r3-sw5 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 p2p-p2mp connected-prefixes include +! +interface r3-sw6 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 p2p-p2mp connected-prefixes include +! +interface r3-stubnet + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.0 +! +router ospf6 + ospf6 router-id 10.0.0.3 + log-adjacency-changes detail + redistribute static +! +line vty + exec-timeout 0 0 +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r3/show_ipv6_route.ref b/tests/topotests/ospf6_point_to_multipoint/r3/show_ipv6_route.ref new file mode 100644 index 0000000..df949e6 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r3/show_ipv6_route.ref @@ -0,0 +1,12 @@ +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet, weight 1, XX:XX:XX +O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::1/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::2/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, weight 1, XX:XX:XX +O>* fc00:b:b:b::4/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_point_to_multipoint/r3/zebra.conf b/tests/topotests/ospf6_point_to_multipoint/r3/zebra.conf new file mode 100644 index 0000000..3cc5626 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r3/zebra.conf @@ -0,0 +1,23 @@ +! +hostname r3 +log file zebra.log +! +! debug zebra events +! debug zebra rib +! +interface r3-stubnet + ipv6 address fc00:3:3:3::3/64 +! +interface r3-sw5 + ipv6 address fc00:a:a:a::3/64 +! +interface r3-sw6 + ipv6 address fc00:b:b:b::3/64 +! +interface lo +! +ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234 +! +! +line vty +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.nhg.ref b/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.nhg.ref new file mode 100644 index 0000000..ca1e2f0 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.nhg.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fc00:4:4:4::1234 dev r4-stubnet proto XXXX metric 20 pref medium +fc00:4:4:4::/64 dev r4-stubnet proto XXXX metric 256 pref medium +fc00:a:a:a::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::1 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::2 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::3 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 dev r4-sw6 proto XXXX metric 256 pref medium +fc00:b:b:b::3 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.ref b/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.ref new file mode 100644 index 0000000..4f4e72f --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 via fc00:4:4:4::1234 dev r4-stubnet proto XXXX metric 20 pref medium +fc00:4:4:4::/64 dev r4-stubnet proto XXXX metric 256 pref medium +fc00:a:a:a::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::1 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::2 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::3 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 dev r4-sw6 proto XXXX metric 256 pref medium +fc00:b:b:b::3 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r4/ospf6d.conf b/tests/topotests/ospf6_point_to_multipoint/r4/ospf6d.conf new file mode 100644 index 0000000..25b8551 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r4/ospf6d.conf @@ -0,0 +1,30 @@ +hostname r4 +log file ospf6d.log +! +! debug ospf6 message all +! debug ospf6 lsa unknown +! debug ospf6 zebra +! debug ospf6 interface +! debug ospf6 neighbor +! debug ospf6 route table +! debug ospf6 flooding +! +interface r4-sw6 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 p2p-p2mp connected-prefixes include +! +interface r4-stubnet + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.1 +! +router ospf6 + ospf6 router-id 10.0.0.4 + log-adjacency-changes detail + redistribute static +! +line vty + exec-timeout 0 0 +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r4/show_ipv6_route.ref b/tests/topotests/ospf6_point_to_multipoint/r4/show_ipv6_route.ref new file mode 100644 index 0000000..f377ca3 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r4/show_ipv6_route.ref @@ -0,0 +1,13 @@ +O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet, weight 1, XX:XX:XX +O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:a:a:a::1/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:a:a:a::2/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:a:a:a::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, weight 1, XX:XX:XX +O>* fc00:b:b:b::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_point_to_multipoint/r4/zebra.conf b/tests/topotests/ospf6_point_to_multipoint/r4/zebra.conf new file mode 100644 index 0000000..20e27ce --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r4/zebra.conf @@ -0,0 +1,20 @@ +! +hostname r4 +log file zebra.log +! +! debug zebra events +! debug zebra rib +! +interface r4-stubnet + ipv6 address fc00:4:4:4::4/64 +! +interface r4-sw6 + ipv6 address fc00:b:b:b::4/64 +! +interface lo +! +ipv6 route fc00:4444:4444:4444::/64 fc00:4:4:4::1234 +! +! +line vty +! diff --git a/tests/topotests/ospf6_point_to_multipoint/test_ospf6_point_to_multipoint.py b/tests/topotests/ospf6_point_to_multipoint/test_ospf6_point_to_multipoint.py new file mode 100644 index 0000000..142acf1 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/test_ospf6_point_to_multipoint.py @@ -0,0 +1,392 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf6_point_to_multipoint.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +r""" +test_ospf6_point_to_multipoint.py: + + -----\ + SW1 - Stub Net 1 SW2 - Stub Net 2 \ + fc00:1:1:1::/64 fc00:2:2:2::/64 \ +\___________________/ \___________________/ | + | | | + | | | + | ::1 | ::2 | ++---------+---------+ +---------+---------+ | +| R1 | | R2 | | +| FRRouting | | FRRouting | | +| Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | | ++---------+---------+ +---------+---------+ | + | ::1 | ::2 \ + \______ ___________/ OSPFv3 + \ / Area 0.0.0.0 + \ / / + ~~~~~~~~~~~~~~~~~~ | + ~~ SW5 ~~ | + ~~ Switch ~~ | + ~~ fc00:A:A:A::/64 ~~ | + ~~~~~~~~~~~~~~~~~~ | + | /---- | + | ::3 | SW3 - Stub Net 3 | + +---------+---------+ /-+ fc00:3:3:3::/64 | + | R3 | / | / + | FRRouting +--/ \---- / + | Rtr-ID: 10.0.0.3 | ::3 ___________/ + +---------+---------+ \ + | ::3 \ + | \ + ~~~~~~~~~~~~~~~~~~ | + ~~ SW6 ~~ | + ~~ Switch ~~ | + ~~ fc00:B:B:B::/64 ~~ \ + ~~~~~~~~~~~~~~~~~~ OSPFv3 + | Area 0.0.0.1 + | ::4 / + +---------+---------+ /---- | + | R4 | | SW4 - Stub Net 4 | + | FRRouting +------+ fc00:4:4:4::/64 | + | Rtr-ID: 10.0.0.4 | ::4 | / + +-------------------+ \---- / + -----/ +""" + +import os +import re +import sys +import pytest + +from functools import partial + + +# Save the Current Working Directory to find configuration files later. +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.ospfd] + + +def build_topo(tgen): + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + # + # Wire up the switches and routers + # Note that we specify the link names so we match the config files + # + + # Create a empty network for router 1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"], nodeif="r1-stubnet") + + # Create a empty network for router 2 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"], nodeif="r2-stubnet") + + # Create a empty network for router 3 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"], nodeif="r3-stubnet") + + # Create a empty network for router 4 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r4"], nodeif="r4-stubnet") + + # Interconnect routers 1, 2, and 3 + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"], nodeif="r1-sw5") + switch.add_link(tgen.gears["r2"], nodeif="r2-sw5") + switch.add_link(tgen.gears["r3"], nodeif="r3-sw5") + + # Interconnect routers 3 and 4 + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r3"], nodeif="r3-sw6") + switch.add_link(tgen.gears["r4"], nodeif="r4-sw6") + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + logger.info("** %s: Setup Topology" % mod.__name__) + logger.info("******************************************") + + # For debugging after starting net, but before starting FRR, + # uncomment the next line + # tgen.mininet_cli() + + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + # For debugging after starting FRR daemons, uncomment the next line + # tgen.mininet_cli() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_wait_protocol_convergence(): + "Wait for OSPFv3 to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_neighbor_full(router, neighbor): + "Wait until OSPFv3 convergence." + logger.info("waiting OSPFv3 router '{}'".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ipv6 ospf6 neighbor json", + {"neighbors": [{"neighborId": neighbor, "state": "Full"}]}, + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + expect_neighbor_full("r1", "10.0.0.2") + expect_neighbor_full("r1", "10.0.0.3") + + expect_neighbor_full("r2", "10.0.0.1") + expect_neighbor_full("r2", "10.0.0.3") + + expect_neighbor_full("r3", "10.0.0.1") + expect_neighbor_full("r3", "10.0.0.2") + expect_neighbor_full("r3", "10.0.0.4") + + expect_neighbor_full("r4", "10.0.0.3") + + +def compare_show_ipv6(rname, expected): + """ + Calls 'show ipv6 route' for router `rname` and compare the obtained + result with the expected output. + """ + tgen = get_topogen() + + # Use the vtysh output, with some masking to make comparison easy + current = topotest.ip6_route_zebra(tgen.gears[rname]) + + # Use just the 'O'spf lines of the output + linearr = [] + for line in current.splitlines(): + if re.match("^O", line): + linearr.append(line) + + current = "\n".join(linearr) + + return topotest.difflines( + topotest.normalize_text(current), + topotest.normalize_text(expected), + title1="Current output", + title2="Expected output", + ) + + +def test_ospfv3_routingTable(): + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # For debugging, uncomment the next line + # tgen.mininet_cli() + + # Verify OSPFv3 Routing Table + for router, rnode in tgen.routers().items(): + logger.info('Waiting for router "%s" convergence', router) + + # Load expected results from the command + reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router)) + expected = open(reffile).read() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(compare_show_ipv6, router, expected) + result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5) + assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff) + + +def test_linux_ipv6_kernel_routingTable(): + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # Verify Linux Kernel Routing Table + logger.info("Verifying Linux IPv6 Kernel Routing Table") + + failures = 0 + + # Get a list of all current link-local addresses first as they change for + # each run and we need to translate them + linklocals = [] + for i in range(1, 5): + linklocals += tgen.net["r{}".format(i)].get_ipv6_linklocal() + + # Now compare the routing tables (after substituting link-local addresses) + + for i in range(1, 5): + # Actual output from router + actual = tgen.gears["r{}".format(i)].run("ip -6 route").rstrip() + if "nhid" in actual: + refTableFile = os.path.join(CWD, "r{}/ip_6_address.nhg.ref".format(i)) + else: + refTableFile = os.path.join(CWD, "r{}/ip_6_address.ref".format(i)) + + if os.path.isfile(refTableFile): + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ("\n".join(expected.splitlines())).splitlines(1) + + # Mask out Link-Local mac addresses + for ll in linklocals: + actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0]) + # Mask out protocol name or number + actual = re.sub(r"[ ]+proto [0-9a-z]+ +", " proto XXXX ", actual) + actual = re.sub(r"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual) + # Remove ff00::/8 routes (seen on some kernels - not from FRR) + actual = re.sub(r"ff00::/8.*", "", actual) + + # Strip empty lines + actual = actual.lstrip() + actual = actual.rstrip() + actual = re.sub(r" +", " ", actual) + + filtered_lines = [] + for line in sorted(actual.splitlines()): + if line.startswith("fe80::/64 ") or line.startswith( + "unreachable fe80::/64 " + ): + continue + filtered_lines.append(line) + actual = "\n".join(filtered_lines).splitlines(1) + + # Print Actual table + # logger.info("Router r%s table" % i) + # for line in actual: + # logger.info(line.rstrip()) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual OSPFv3 IPv6 routing table", + title2="expected OSPFv3 IPv6 routing table", + ) + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + sys.stderr.write( + "r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n" + % (i, diff) + ) + failures += 1 + else: + logger.info("r%s ok" % i) + + assert failures == 0, ( + "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s" + % (i, diff) + ) + else: + logger.error("r{} failed - no nhid ref file: {}".format(i, refTableFile)) + + assert False, ( + "Linux Kernel IPv6 Routing Table verification failed for router r%s\n" + % (i) + ) + + +def test_shutdown_check_stderr(): + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + logger.info( + "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n" + ) + pytest.skip("Skipping test for Stderr output") + + net = tgen.net + + logger.info("\n\n** Verifying unexpected STDERR output from daemons") + logger.info("******************************************") + + for i in range(1, 5): + net["r%s" % i].stopRouter() + log = net["r%s" % i].getStdErr("ospf6d") + if log: + logger.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log)) + log = net["r%s" % i].getStdErr("zebra") + if log: + logger.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) + + +def test_shutdown_check_memleak(): + "Run the memory leak test and report results." + + if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None: + logger.info( + "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)" + ) + pytest.skip("Skipping test for memory leaks") + + tgen = get_topogen() + + net = tgen.net + + for i in range(1, 5): + net["r%s" % i].stopRouter() + net["r%s" % i].report_memory_leaks( + os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__) + ) + + +if __name__ == "__main__": + + # To suppress tracebacks, either use the following pytest call or + # add "--tb=no" to cli + # retval = pytest.main(["-s", "--tb=no"]) + + retval = pytest.main(["-s"]) + sys.exit(retval) diff --git a/tests/topotests/ospf6_topo1/r1/ospf6d.conf b/tests/topotests/ospf6_topo1/r1/ospf6d.conf index 5f1ceee..d2693ec 100644 --- a/tests/topotests/ospf6_topo1/r1/ospf6d.conf +++ b/tests/topotests/ospf6_topo1/r1/ospf6d.conf @@ -10,11 +10,13 @@ log file ospf6d.log ! debug ospf6 flooding ! interface r1-stubnet + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! interface r1-sw5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -23,8 +25,6 @@ router ospf6 ospf6 router-id 10.0.0.1 log-adjacency-changes detail redistribute static - interface r1-stubnet area 0.0.0.0 - interface r1-sw5 area 0.0.0.0 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf6_topo1/r2/ospf6d.conf b/tests/topotests/ospf6_topo1/r2/ospf6d.conf index d51b41e..c9e88f1 100644 --- a/tests/topotests/ospf6_topo1/r2/ospf6d.conf +++ b/tests/topotests/ospf6_topo1/r2/ospf6d.conf @@ -10,11 +10,13 @@ log file ospf6d.log ! debug ospf6 flooding ! interface r2-stubnet + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! interface r2-sw5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -23,8 +25,6 @@ router ospf6 ospf6 router-id 10.0.0.2 log-adjacency-changes detail redistribute static - interface r2-stubnet area 0.0.0.0 - interface r2-sw5 area 0.0.0.0 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf6_topo1/r3/ospf6d.conf b/tests/topotests/ospf6_topo1/r3/ospf6d.conf index cad71ac..e1c3e44 100644 --- a/tests/topotests/ospf6_topo1/r3/ospf6d.conf +++ b/tests/topotests/ospf6_topo1/r3/ospf6d.conf @@ -10,16 +10,19 @@ log file ospf6d.log ! debug ospf6 flooding ! interface r3-stubnet + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! interface r3-sw5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! interface r3-sw6 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -28,9 +31,6 @@ router ospf6 ospf6 router-id 10.0.0.3 log-adjacency-changes detail redistribute static - interface r3-stubnet area 0.0.0.0 - interface r3-sw5 area 0.0.0.0 - interface r3-sw6 area 0.0.0.1 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf6_topo1/r4/ospf6d.conf b/tests/topotests/ospf6_topo1/r4/ospf6d.conf index f0b166b..230ec7a 100644 --- a/tests/topotests/ospf6_topo1/r4/ospf6d.conf +++ b/tests/topotests/ospf6_topo1/r4/ospf6d.conf @@ -10,11 +10,13 @@ log file ospf6d.log ! debug ospf6 flooding ! interface r4-stubnet + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! interface r4-sw6 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -23,8 +25,6 @@ router ospf6 ospf6 router-id 10.0.0.4 log-adjacency-changes detail redistribute static - interface r4-stubnet area 0.0.0.1 - interface r4-sw6 area 0.0.0.1 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json b/tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json index c0a1f6a..ebb3848 100644 --- a/tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json +++ b/tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json @@ -8,6 +8,6 @@ "type":"sharp" } ], - "routesTotal":4, - "routesTotalFib":4 + "routesTotal":5, + "routesTotalFib":5 } diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf index e365e25..9959581 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf @@ -5,12 +5,18 @@ log file /tmp/r1-frr.log ! interface r1-eth0 ip address 10.0.1.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! interface r1-eth1 ip address 10.0.20.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! interface r1-eth2 vrf neno ip address 10.0.30.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! ip forwarding ! diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt index 86c089a..248375d 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt @@ -1,9 +1,11 @@ O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX +L>* 10.0.1.1/32 is directly connected, r1-eth0, XX:XX:XX O>* 10.0.2.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX B>* 10.0.3.0/24 [20/20] via 10.0.30.3, r1-eth2 (vrf neno), weight 1, XX:XX:XX O>* 10.0.4.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX O 10.0.20.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX C>* 10.0.20.0/24 is directly connected, r1-eth1, XX:XX:XX -B>* 10.0.30.0/24 [20/0] is directly connected, r1-eth2 (vrf neno), weight 1, XX:XX:XX +L>* 10.0.20.1/32 is directly connected, r1-eth1, XX:XX:XX +B>* 10.0.30.0/24 [20/0] is directly connected, neno (vrf neno), weight 1, XX:XX:XX O>* 10.0.40.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt index 4e818eb..6e13352 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt @@ -3,4 +3,5 @@ O>* 10.0.3.0/24 [110/20] via 10.0.30.3, r1-eth2, weight 1, XX:XX:XX B>* 10.0.4.0/24 [20/20] via 10.0.20.2, r1-eth1 (vrf default), weight 1, XX:XX:XX O 10.0.30.0/24 [110/10] is directly connected, r1-eth2, weight 1, XX:XX:XX C>* 10.0.30.0/24 is directly connected, r1-eth2, XX:XX:XX +L>* 10.0.30.1/32 is directly connected, r1-eth2, XX:XX:XX B>* 10.0.40.0/24 [20/20] via 10.0.20.2, r1-eth1 (vrf default), weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf index e87899c..29909de 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf @@ -5,14 +5,20 @@ log file /tmp/r2-frr.log ! interface r2-eth0 ip address 10.0.2.2/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! interface r2-eth1 ip address 10.0.20.2/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! ip route 0.0.0.0/0 10.0.20.1 ! interface r2-eth2 vrf ray ip address 10.0.40.2/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! ip forwarding ! diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt index 9681d8a..d7d3143 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt @@ -2,9 +2,11 @@ S>* 0.0.0.0/0 [1/0] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX O>* 10.0.1.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX +L>* 10.0.2.2/32 is directly connected, r2-eth0, XX:XX:XX O>* 10.0.3.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX B>* 10.0.4.0/24 [20/20] via 10.0.40.4, r2-eth2 (vrf ray), weight 1, XX:XX:XX O 10.0.20.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX C>* 10.0.20.0/24 is directly connected, r2-eth1, XX:XX:XX +L>* 10.0.20.2/32 is directly connected, r2-eth1, XX:XX:XX O>* 10.0.30.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX -B>* 10.0.40.0/24 [20/0] is directly connected, r2-eth2 (vrf ray), weight 1, XX:XX:XX +B>* 10.0.40.0/24 [20/0] is directly connected, ray (vrf ray), weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt index ce9903a..6ab1bb8 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt @@ -1,9 +1,10 @@ VRF ray: B 10.0.1.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default) inactive, weight 1, XX:XX:XX -B 10.0.2.0/24 [20/0] is directly connected, r2-eth0 (vrf default) inactive, weight 1, XX:XX:XX +B 10.0.2.0/24 [20/0] is directly connected, lo (vrf default) inactive, weight 1, XX:XX:XX B>* 10.0.3.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default), weight 1, XX:XX:XX O>* 10.0.4.0/24 [110/20] via 10.0.40.4, r2-eth2, weight 1, XX:XX:XX -B 10.0.20.0/24 [20/0] is directly connected, r2-eth1 (vrf default) inactive, weight 1, XX:XX:XX +B 10.0.20.0/24 [20/0] is directly connected, lo (vrf default) inactive, weight 1, XX:XX:XX B>* 10.0.30.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default), weight 1, XX:XX:XX O 10.0.40.0/24 [110/10] is directly connected, r2-eth2, weight 1, XX:XX:XX C>* 10.0.40.0/24 is directly connected, r2-eth2, XX:XX:XX +L>* 10.0.40.2/32 is directly connected, r2-eth2, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf index 2657f58..35fe22e 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf @@ -5,9 +5,13 @@ log file /tmp/r3-frr.log ! interface r3-eth0 ip address 10.0.3.3/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! interface r3-eth1 ip address 10.0.30.3/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! ip forwarding ! diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt index f6f861b..b1701fe 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt @@ -1,8 +1,10 @@ O 10.0.3.0/24 [110/10] is directly connected, r3-eth0, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r3-eth0, XX:XX:XX +L>* 10.0.3.3/32 is directly connected, r3-eth0, XX:XX:XX O>* 10.0.4.0/24 [110/20] via 10.0.30.1, r3-eth1, weight 1, XX:XX:XX O 10.0.30.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX C>* 10.0.30.0/24 is directly connected, r3-eth1, XX:XX:XX +L>* 10.0.30.3/32 is directly connected, r3-eth1, XX:XX:XX O>* 10.0.40.0/24 [110/20] via 10.0.30.1, r3-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf index 79d8077..721c3d9 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf @@ -5,9 +5,13 @@ log file /tmp/r4-frr.log ! interface r4-eth0 ip address 10.0.4.4/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! interface r4-eth1 ip address 10.0.40.4/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! ip forwarding ! diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt index b6be5e7..3723a8a 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt @@ -1,7 +1,9 @@ O>* 10.0.3.0/24 [110/20] via 10.0.40.2, r4-eth1, weight 1, XX:XX:XX O 10.0.4.0/24 [110/10] is directly connected, r4-eth0, weight 1, XX:XX:XX C>* 10.0.4.0/24 is directly connected, r4-eth0, XX:XX:XX +L>* 10.0.4.4/32 is directly connected, r4-eth0, XX:XX:XX O>* 10.0.30.0/24 [110/20] via 10.0.40.2, r4-eth1, weight 1, XX:XX:XX O 10.0.40.0/24 [110/10] is directly connected, r4-eth1, weight 1, XX:XX:XX C>* 10.0.40.0/24 is directly connected, r4-eth1, XX:XX:XX +L>* 10.0.40.4/32 is directly connected, r4-eth1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r1/ospfd.conf b/tests/topotests/ospf_netns_vrf/r1/ospfd.conf index e1e2bfb..ba13146 100644 --- a/tests/topotests/ospf_netns_vrf/r1/ospfd.conf +++ b/tests/topotests/ospf_netns_vrf/r1/ospfd.conf @@ -3,6 +3,14 @@ hostname r1 password zebra log file /tmp/r1-ospfd.log ! +interface r1-eth0 vrf r1-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 +! +interface r1-eth1 vrf r1-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 +! router ospf vrf r1-ospf-cust1 ospf router-id 10.0.255.1 redistribute kernel diff --git a/tests/topotests/ospf_netns_vrf/r1/zebraroute.txt b/tests/topotests/ospf_netns_vrf/r1/zebraroute.txt index 979af20..bf874ac 100644 --- a/tests/topotests/ospf_netns_vrf/r1/zebraroute.txt +++ b/tests/topotests/ospf_netns_vrf/r1/zebraroute.txt @@ -1,8 +1,10 @@ VRF r1-ospf-cust1: O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX +L>* 10.0.1.1/32 is directly connected, r1-eth0, XX:XX:XX O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, weight 1, XX:XX:XX O 10.0.3.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r1-eth1, XX:XX:XX +L>* 10.0.3.2/32 is directly connected, r1-eth1, XX:XX:XX O>* 10.0.10.0/24 [110/20] via 10.0.3.1, r1-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt b/tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt index ec99fad..e515205 100644 --- a/tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt +++ b/tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt @@ -1,7 +1,9 @@ VRF r1-ospf-cust1: O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX +L>* 10.0.1.1/32 is directly connected, r1-eth0, XX:XX:XX O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, weight 1, XX:XX:XX O 10.0.3.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r1-eth1, XX:XX:XX +L>* 10.0.3.2/32 is directly connected, r1-eth1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r2/ospfd.conf b/tests/topotests/ospf_netns_vrf/r2/ospfd.conf index c198427..01b6b15 100644 --- a/tests/topotests/ospf_netns_vrf/r2/ospfd.conf +++ b/tests/topotests/ospf_netns_vrf/r2/ospfd.conf @@ -3,6 +3,13 @@ hostname r2 password zebra log file /tmp/r2-ospfd.log ! +interface r2-eth0 vrf r2-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 +! +interface r2-eth1 vrf r2-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! router ospf vrf r2-ospf-cust1 ospf router-id 10.0.255.2 diff --git a/tests/topotests/ospf_netns_vrf/r2/zebraroute.txt b/tests/topotests/ospf_netns_vrf/r2/zebraroute.txt index df66e92..3763ef8 100644 --- a/tests/topotests/ospf_netns_vrf/r2/zebraroute.txt +++ b/tests/topotests/ospf_netns_vrf/r2/zebraroute.txt @@ -2,7 +2,9 @@ VRF r2-ospf-cust1: O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, weight 1, XX:XX:XX O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX +L>* 10.0.2.1/32 is directly connected, r2-eth0, XX:XX:XX O 10.0.3.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r2-eth1, XX:XX:XX +L>* 10.0.3.3/32 is directly connected, r2-eth1, XX:XX:XX O>* 10.0.10.0/24 [110/20] via 10.0.3.1, r2-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt b/tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt index 4afc354..f6eaba6 100644 --- a/tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt +++ b/tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt @@ -2,6 +2,8 @@ VRF r2-ospf-cust1: O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, weight 1, XX:XX:XX O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX +L>* 10.0.2.1/32 is directly connected, r2-eth0, XX:XX:XX O 10.0.3.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r2-eth1, XX:XX:XX +L>* 10.0.3.3/32 is directly connected, r2-eth1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r3/ospfd.conf b/tests/topotests/ospf_netns_vrf/r3/ospfd.conf index b73d547..abfaa5b 100644 --- a/tests/topotests/ospf_netns_vrf/r3/ospfd.conf +++ b/tests/topotests/ospf_netns_vrf/r3/ospfd.conf @@ -4,6 +4,14 @@ password zebra log file /tmp/r3-ospfd.log ! ! +interface r3-eth0 vrf r3-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 +! +interface r3-eth1 vrf r3-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 +! router ospf vrf r3-ospf-cust1 ospf router-id 10.0.255.3 redistribute kernel diff --git a/tests/topotests/ospf_netns_vrf/r3/zebraroute.txt b/tests/topotests/ospf_netns_vrf/r3/zebraroute.txt index b435c2e..5eb92ef 100644 --- a/tests/topotests/ospf_netns_vrf/r3/zebraroute.txt +++ b/tests/topotests/ospf_netns_vrf/r3/zebraroute.txt @@ -3,6 +3,8 @@ O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r3-eth0, weight 1, XX:XX:XX O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r3-eth0, weight 1, XX:XX:XX O 10.0.3.0/24 [110/10] is directly connected, r3-eth0, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r3-eth0, XX:XX:XX +L>* 10.0.3.1/32 is directly connected, r3-eth0, XX:XX:XX O 10.0.10.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX C>* 10.0.10.0/24 is directly connected, r3-eth1, XX:XX:XX +L>* 10.0.10.1/32 is directly connected, r3-eth1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt b/tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt index f30a4be..26cc196 100644 --- a/tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt +++ b/tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt @@ -1,4 +1,5 @@ VRF r3-ospf-cust1: O 10.0.10.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX C>* 10.0.10.0/24 is directly connected, r3-eth1, XX:XX:XX +L>* 10.0.10.1/32 is directly connected, r3-eth1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py b/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py index 2716f63..23eef8f 100644 --- a/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py +++ b/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py @@ -87,6 +87,7 @@ def setup_module(mod): router.net.set_intf_netns(rname + "-eth0", ns, up=True) router.net.set_intf_netns(rname + "-eth1", ns, up=True) + router.load_config(TopoRouter.RD_MGMTD, None, "--vrfwnetns") router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), diff --git a/tests/topotests/ospf_suppress_fa/r1/initial.json b/tests/topotests/ospf_suppress_fa/r1/initial.json new file mode 100644 index 0000000..f0e6110 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r1/initial.json @@ -0,0 +1,44 @@ +{ + "routerId":"10.0.12.1", + "asExternalLinkStates":[ + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.1.1", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"10.0.23.3", + "externalRouteTag":0 + }, + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.2.2", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"10.0.23.3", + "externalRouteTag":0 + }, + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.3.3", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"10.0.23.3", + "externalRouteTag":0 + } + ] +} diff --git a/tests/topotests/ospf_suppress_fa/r1/neighbor.json b/tests/topotests/ospf_suppress_fa/r1/neighbor.json new file mode 100644 index 0000000..417fcbe --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r1/neighbor.json @@ -0,0 +1,13 @@ +{ + "neighbors":{ + "10.0.23.2":[ + { + "nbrPriority":1, + "converged":"Full", + "role":"DROther", + "ifaceAddress":"10.0.12.2", + "ifaceName":"r1-eth0:10.0.12.1" + } + ] + } +} diff --git a/tests/topotests/ospf_suppress_fa/r1/post.json b/tests/topotests/ospf_suppress_fa/r1/post.json new file mode 100644 index 0000000..98cbf8f --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r1/post.json @@ -0,0 +1,44 @@ +{ + "routerId":"10.0.12.1", + "asExternalLinkStates":[ + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.1.1", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"0.0.0.0", + "externalRouteTag":0 + }, + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.2.2", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"0.0.0.0", + "externalRouteTag":0 + }, + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.3.3", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"0.0.0.0", + "externalRouteTag":0 + } + ] +} 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 d5583ac..8b3fc58 100644 --- a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py +++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py @@ -22,6 +22,8 @@ test_ospf_unset_suppress_fa() import os import sys +import json +from functools import partial import re import pytest @@ -33,6 +35,7 @@ sys.path.append(os.path.join(CWD, "../")) # 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. @@ -75,6 +78,7 @@ def setup_module(mod): TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) ) + logger.info("Module Setup") tgen.start_router() @@ -93,7 +97,17 @@ def test_converge_protocols(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - topotest.sleep(10, "Waiting for OSPF convergence") + router = tgen.gears["r1"] + json_file = "{}/r1/neighbor.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show ip ospf neighbor json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "r1 has not converged" + + logger.info("Converged Protocol") def ospf_configure_suppress_fa(router_name, area): @@ -114,58 +128,55 @@ def ospf_unconfigure_suppress_fa(router_name, area): router.vtysh_cmd("conf t\nrouter ospf\narea {} nssa\nexit\n".format(area)) -def ospf_get_lsa_type5(router_name): - "Return a dict with link state id as key and forwarding addresses as value" - - result = dict() - tgen = get_topogen() - router = tgen.gears[router_name] - cmd = "show ip ospf database external\n" - output = topotest.normalize_text(router.vtysh_cmd(cmd)) - for line in output.splitlines(): - re0 = re.match(r"\s+Link State ID: (\S+) \(External Network Number\)", line) - if re0: - lsa = re0.group(1) - re1 = re.match(r"\s+Forward Address: (\S+)", line) - if re1: - result[lsa] = re1.group(1) - return result - - -@pytest.fixture(scope="module", name="original") def test_ospf_set_suppress_fa(): "Test OSPF area [x] nssa suppress-fa" + logger.info("Testing Turning on/off suppress-fa") + tgen = get_topogen() + # Get current forwarding address for each LSA type-5 in r1 - initial = ospf_get_lsa_type5("r1") + logger.info("Get Initial State") + router = tgen.gears["r1"] + json_file = "{}/r1/initial.json".format(CWD) + expected_initial = json.loads(open(json_file).read()) + + test_func_initial = partial( + topotest.router_json_cmp, + router, + "show ip ospf data external json", + expected_initial, + ) + _, result = topotest.run_and_expect(test_func_initial, None, count=30, wait=1) + assert result is None, "Unable to get expected initial states" + logger.info("Configure suppress-fa") # Configure suppres-fa in r2 area 1 ospf_configure_suppress_fa("r2", "1") - topotest.sleep(10, "Waiting for OSPF convergence") - # Check forwarding address on r1 for all statics is 0.0.0.0 - assertmsg = "Forwarding address is not 0.0.0.0 after enabling OSPF suppress-fa" - suppress = ospf_get_lsa_type5("r1") - for prefix in suppress: - assert suppress[prefix] == "0.0.0.0", assertmsg + logger.info("Ensure that OSPF has converged on new values") + json_file = "{}/r1/post.json".format(CWD) + expected_post = json.loads(open(json_file).read()) - # Return the original forwarding addresses so we can compare them - # in the test_ospf_unset_supress_fa - return initial + test_func_post = partial( + topotest.router_json_cmp, + router, + "show ip ospf data external json", + expected_post, + ) + _, result = topotest.run_and_expect(test_func_post, None, count=30, wait=1) + assert result is None, "Unable to get expected state after turning on suppress-fa" -def test_ospf_unset_supress_fa(original): - "Test OSPF no area [x] nssa suppress-fa" + logger.info("Test OSPF no area [x] nssa suppress-fa") # Remove suppress-fa in r2 area 1 ospf_unconfigure_suppress_fa("r2", "1") - topotest.sleep(10, "Waiting for OSPF convergence") - # Check forwarding address is the original value on r1 for all statics - assertmsg = "Forwarding address is not correct after removing OSPF suppress-fa" - restore = ospf_get_lsa_type5("r1") - for prefix in restore: - assert restore[prefix] == original[prefix], assertmsg + logger.info("Has OSPF returned to original values") + _, result = topotest.run_and_expect(test_func_post, None, count=30, wait=1) + assert ( + result is None + ), "Unable to return to original state after turning off suppress-fa" if __name__ == "__main__": diff --git a/tests/topotests/ospf_te_topo1/r1/ospfd.conf b/tests/topotests/ospf_te_topo1/r1/ospfd.conf index 312dd26..1541f3f 100644 --- a/tests/topotests/ospf_te_topo1/r1/ospfd.conf +++ b/tests/topotests/ospf_te_topo1/r1/ospfd.conf @@ -2,13 +2,13 @@ interface lo ip ospf area 0.0.0.0 ! -interface r1-eth0 +interface eth0 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 ip ospf area 0.0.0.0 ! -interface r1-eth1 +interface eth1 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 diff --git a/tests/topotests/ospf_te_topo1/r1/zebra.conf b/tests/topotests/ospf_te_topo1/r1/zebra.conf index c47789a..dc50e3d 100644 --- a/tests/topotests/ospf_te_topo1/r1/zebra.conf +++ b/tests/topotests/ospf_te_topo1/r1/zebra.conf @@ -2,7 +2,7 @@ interface lo ip address 10.0.255.1/32 ! -interface r1-eth0 +interface eth0 ip address 10.0.0.1/24 link-params metric 20 @@ -12,7 +12,7 @@ interface r1-eth0 enable exit-link-params ! -interface r1-eth1 +interface eth1 ip address 10.0.1.1/24 link-params enable diff --git a/tests/topotests/ospf_te_topo1/r2/ospfd.conf b/tests/topotests/ospf_te_topo1/r2/ospfd.conf index e9c3f65..acc2e6b 100644 --- a/tests/topotests/ospf_te_topo1/r2/ospfd.conf +++ b/tests/topotests/ospf_te_topo1/r2/ospfd.conf @@ -2,25 +2,25 @@ interface lo ip ospf area 0.0.0.0 ! -interface r2-eth0 +interface eth0 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 ip ospf area 0.0.0.0 ! -interface r2-eth1 +interface eth1 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 ip ospf area 0.0.0.0 ! -interface r2-eth2 +interface eth2 ip ospf network point-to-point ip ospf area 0.0.0.0 ip ospf hello-interval 2 ip ospf dead-interval 10 ! -interface r2-eth3 +interface eth3 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 diff --git a/tests/topotests/ospf_te_topo1/r2/zebra.conf b/tests/topotests/ospf_te_topo1/r2/zebra.conf index a9771f7..6fe4dd0 100644 --- a/tests/topotests/ospf_te_topo1/r2/zebra.conf +++ b/tests/topotests/ospf_te_topo1/r2/zebra.conf @@ -2,25 +2,25 @@ interface lo ip address 10.0.255.2/32 ! -interface r2-eth0 +interface eth0 ip address 10.0.0.2/24 link-params enable exit-link-params ! -interface r2-eth1 +interface eth1 ip address 10.0.1.2/24 link-params enable exit-link-params ! -interface r2-eth2 +interface eth2 ip address 10.0.3.2/24 link-params enable exit-link-params ! -interface r2-eth3 +interface eth3 ip address 10.0.4.2/24 link-params metric 30 diff --git a/tests/topotests/ospf_te_topo1/r3/ospfd.conf b/tests/topotests/ospf_te_topo1/r3/ospfd.conf index caa5f1e..fc94437 100644 --- a/tests/topotests/ospf_te_topo1/r3/ospfd.conf +++ b/tests/topotests/ospf_te_topo1/r3/ospfd.conf @@ -2,13 +2,13 @@ interface lo ip ospf area 0.0.0.0 ! -interface r3-eth0 +interface eth0 ip ospf network point-to-point ip ospf area 0.0.0.0 ip ospf hello-interval 2 ip ospf dead-interval 10 ! -interface r3-eth1 +interface eth1 ip ospf network point-to-point ip ospf area 0.0.0.0 ip ospf hello-interval 2 diff --git a/tests/topotests/ospf_te_topo1/r3/zebra.conf b/tests/topotests/ospf_te_topo1/r3/zebra.conf index 4cf9077..262b5ad 100644 --- a/tests/topotests/ospf_te_topo1/r3/zebra.conf +++ b/tests/topotests/ospf_te_topo1/r3/zebra.conf @@ -2,14 +2,14 @@ interface lo ip address 10.0.255.3/32 ! -interface r3-eth0 +interface eth0 ip address 10.0.3.1/24 link-params enable admin-grp 0x20 exit-link-params ! -interface r3-eth1 +interface eth1 ip address 10.0.5.1/24 link-params enable diff --git a/tests/topotests/ospf_te_topo1/r4/ospfd.conf b/tests/topotests/ospf_te_topo1/r4/ospfd.conf index cd50801..5ec7918 100644 --- a/tests/topotests/ospf_te_topo1/r4/ospfd.conf +++ b/tests/topotests/ospf_te_topo1/r4/ospfd.conf @@ -2,7 +2,7 @@ interface lo ip ospf area 0.0.0.0 ! -interface r4-eth0 +interface eth0 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 diff --git a/tests/topotests/ospf_te_topo1/r4/zebra.conf b/tests/topotests/ospf_te_topo1/r4/zebra.conf index 18c003b..c482685 100644 --- a/tests/topotests/ospf_te_topo1/r4/zebra.conf +++ b/tests/topotests/ospf_te_topo1/r4/zebra.conf @@ -2,7 +2,7 @@ interface lo ip address 10.0.255.4/32 ! -interface r4-eth0 +interface eth0 ip address 10.0.4.1/24 link-params enable diff --git a/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py b/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py index c8533cf..11fecc3 100644 --- a/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py +++ b/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py @@ -18,22 +18,22 @@ test_ospf_te_topo1.py: Test the FRR OSPF with Traffic Engineering. | 10.0.225.1 | | | +------------+ - r1-eth0| |r1-eth1 + eth0| |eth1 | | 10.0.0.0/24| |10.0.1.0/24 | | - r2-eth0| |r2-eth1 + eth0| |eth1 +------------+ +------------+ | | | | - | R2 |r2-eth2 r3-eth0| R3 | + | R2 |eth2 eth0| R3 | | 10.0.255.2 +------------------+ 10.0.255.3 | | | 10.0.3.0/24 | | +------------+ +------+-----+ - r2-eth3| r3-eth1| + eth3| eth1| | | 10.0.4.0/24| 10.0.5.0/24| | | - r4-eth0| V + eth0| V +------------+ ASBR 10.0.255.5 | | | R4 | @@ -70,30 +70,24 @@ def build_topo(tgen): "Build function" # Create 4 routers - for routern in range(1, 5): - tgen.add_router("r{}".format(routern)) + r1 = tgen.add_router("r1") + r2 = tgen.add_router("r2") + r3 = tgen.add_router("r3") + r4 = tgen.add_router("r4") # Interconect router 1 and 2 with 2 links - switch = tgen.add_switch("s1") - switch.add_link(tgen.gears["r1"]) - switch.add_link(tgen.gears["r2"]) - switch = tgen.add_switch("s2") - switch.add_link(tgen.gears["r1"]) - switch.add_link(tgen.gears["r2"]) + tgen.add_link(r1, r2, ifname1="eth0", ifname2="eth0") + tgen.add_link(r1, r2, ifname1="eth1", ifname2="eth1") # Interconect router 3 and 2 - switch = tgen.add_switch("s3") - switch.add_link(tgen.gears["r3"]) - switch.add_link(tgen.gears["r2"]) + tgen.add_link(r2, r3, ifname1="eth2", ifname2="eth0") # Interconect router 4 and 2 - switch = tgen.add_switch("s4") - switch.add_link(tgen.gears["r4"]) - switch.add_link(tgen.gears["r2"]) + tgen.add_link(r2, r4, ifname1="eth3", ifname2="eth0") # Interconnect router 3 with next AS - switch = tgen.add_switch("s5") - switch.add_link(tgen.gears["r3"]) + s1 = tgen.add_switch("s1") + tgen.add_link(r3, s1, ifname1="eth1", ifname2="eth0") def setup_module(mod): @@ -174,8 +168,7 @@ def test_step2(): tgen = setup_testcase("Step2: Shutdown interface between r1 & r2") - tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth1" -c "shutdown"') - tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth1" -c "shutdown"') + tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface eth1" -c "shutdown"') for rname in ["r1", "r2", "r3", "r4"]: compare_ted_json_output(tgen, rname, "ted_step2.json") @@ -227,28 +220,27 @@ def test_step5(): tgen = setup_testcase("Step5: Re-enable interface between r1 & r2") - tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth1" -c "no shutdown"') - tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth1" -c "no shutdown"') + tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface eth1" -c "no shutdown"') for rname in ["r1", "r2", "r3", "r4"]: compare_ted_json_output(tgen, rname, "ted_step5.json") def test_step6(): - "Step6: Set delay and jitter for interface r4-eth0 on r4, remove use-bw \ - for interface r2-eth3 on r2 and verify that corresponding Edges are \ + "Step6: Set delay and jitter for interface eth0 on r4, remove use-bw \ + for interface eth3 on r2 and verify that corresponding Edges are \ updated in the TED on all routers" tgen = setup_testcase("Step6: Modify link parameters on r2 & r4") tgen.net["r2"].cmd( - 'vtysh -c "conf t" -c "interface r2-eth3" -c "link-params" -c "no use-bw"' + 'vtysh -c "conf t" -c "interface eth3" -c "link-params" -c "no use-bw"' ) tgen.net["r4"].cmd( - 'vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay 20000"' + 'vtysh -c "conf t" -c "interface eth0" -c "link-params" -c "delay 20000"' ) tgen.net["r4"].cmd( - 'vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay-variation 10000"' + 'vtysh -c "conf t" -c "interface eth0" -c "link-params" -c "delay-variation 10000"' ) for rname in ["r1", "r2", "r3", "r4"]: diff --git a/tests/topotests/ospf_topo1/r1/ospf6d.conf b/tests/topotests/ospf_topo1/r1/ospf6d.conf index ca3497b..0e6c7da 100644 --- a/tests/topotests/ospf_topo1/r1/ospf6d.conf +++ b/tests/topotests/ospf_topo1/r1/ospf6d.conf @@ -4,10 +4,12 @@ router ospf6 redistribute kernel redistribute connected redistribute static - interface r1-eth0 area 0.0.0.0 - interface r1-eth1 area 0.0.0.0 +! +interface r1-eth0 + ipv6 ospf6 area 0.0.0.0 ! int r1-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 dead-interval 10 ipv6 ospf6 hello-interval 2 ! diff --git a/tests/topotests/ospf_topo1/r2/ospf6d.conf b/tests/topotests/ospf_topo1/r2/ospf6d.conf index 44047e1..f6a1f50 100644 --- a/tests/topotests/ospf_topo1/r2/ospf6d.conf +++ b/tests/topotests/ospf_topo1/r2/ospf6d.conf @@ -4,14 +4,14 @@ router ospf6 redistribute kernel redistribute connected redistribute static - interface r2-eth0 area 0.0.0.0 - interface r2-eth1 area 0.0.0.0 ! int r2-eth0 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! int r2-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! diff --git a/tests/topotests/ospf_topo1/r3/ospf6d.conf b/tests/topotests/ospf_topo1/r3/ospf6d.conf index 13ad9a7..278a016 100644 --- a/tests/topotests/ospf_topo1/r3/ospf6d.conf +++ b/tests/topotests/ospf_topo1/r3/ospf6d.conf @@ -4,19 +4,19 @@ router ospf6 redistribute kernel redistribute connected redistribute static - interface r3-eth0 area 0.0.0.0 - interface r3-eth1 area 0.0.0.0 - interface r3-eth2 area 0.0.0.1 ! int r3-eth0 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! int r3-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! int r3-eth2 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! diff --git a/tests/topotests/ospf_topo1/r4/ospf6d.conf b/tests/topotests/ospf_topo1/r4/ospf6d.conf index f9bde0e..777dd0b 100644 --- a/tests/topotests/ospf_topo1/r4/ospf6d.conf +++ b/tests/topotests/ospf_topo1/r4/ospf6d.conf @@ -4,14 +4,14 @@ router ospf6 redistribute kernel redistribute connected redistribute static - interface r4-eth0 area 0.0.0.1 - interface r4-eth1 area 0.0.0.1 ! int r4-eth0 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! int r4-eth1 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/README.md b/tests/topotests/ospf_unnumbered_point_to_multipoint/README.md new file mode 100644 index 0000000..d9b6817 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/README.md @@ -0,0 +1,41 @@ +# OSPFv2 (IPv4) Topology Test Unumbered (point-to-multipoint over Ethernet) + +## Topology + + SW1 SW2 + \___________________/ \___________________/ + | | + | | + | eth0:10.0.10.1/32 | eth0:10.0.20.1/32 + +---------+---------+ +---------+---------+ + | R1 | | R2 | + | FRRouting | | FRRouting | + | RID: 10.0.255.1 | | RID: 10.0.255.2 | + +---------+---------+ +---------+---------+ + | eth1:10.1.1.2/32 | eth1:10.1.2.2/32 + \______ ___________/ + \ / + \ / + ~~~~~~~~~~~~~~~~~~ + ~~ SW4 ~~ + ~~ Switch ~~ + ~~ ~~ + ~~~~~~~~~~~~~~~~~~ + | + | eth0:10.1.3.2/32 (unumbered) + +---------+---------+ + | R3 | + | FRRouting | + | RID: 10.0.255.3 | + +---------+---------+ + | eth0:10.0.30.1/24 + | + ~~~~~~~~~~~~~~~~~~ + ~~ SW3 ~~ + ~~ Switch ~~ + ~~ ~~ + ~~~~~~~~~~~~~~~~~~ + +## FRR Configuration + +See full config from r1 / r2 / r3 subdirectories diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospf-route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospf-route.json new file mode 100644 index 0000000..563522d --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospf-route.json @@ -0,0 +1 @@ +{"10.0.10.1\/32":{"routeType":"N","transit":false,"cost":10,"area":"0.0.0.0"},"10.0.20.1\/32":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"},"10.0.30.0\/24":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"}} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospfd.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospfd.conf new file mode 100644 index 0000000..c7327b6 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospfd.conf @@ -0,0 +1,11 @@ +! +interface r1-eth1 + ip ospf network point-to-multipoint + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf prefix-suppress 10.1.1.2 +! +router ospf + ospf router-id 10.0.255.1 + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/v4_route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/v4_route.json new file mode 100644 index 0000000..4395b74 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/v4_route.json @@ -0,0 +1,122 @@ +{ + "10.0.10.1\/32": [ + { + "prefix": "10.0.10.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "distance": 110, + "metric": 10, + "table": 254, + "internalStatus": 0, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.0.10.1\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.0.10.1\/32", + "prefixLen": 32, + "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 + } + ], + "10.0.20.1\/32": [ + { + "prefix": "10.0.20.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.0.30.0\/24": [ + { + "prefix": "10.0.30.0\/24", + "prefixLen": 24, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.1.1.2\/32": [ + { + "prefix": "10.1.1.2\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.1.1.2\/32", + "prefixLen": 32, + "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 + } + ] +} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/zebra.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/zebra.conf new file mode 100644 index 0000000..63c75d0 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 10.0.10.1/32 +! +interface r1-eth1 + ip address 10.1.1.2/32 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospf-route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospf-route.json new file mode 100644 index 0000000..8d87e33 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospf-route.json @@ -0,0 +1 @@ +{"10.0.10.1\/32":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"},"10.0.20.1\/32":{"routeType":"N","transit":false,"cost":10,"area":"0.0.0.0"},"10.0.30.0\/24":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"}} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospfd.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospfd.conf new file mode 100644 index 0000000..5d0439f --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospfd.conf @@ -0,0 +1,11 @@ +! +interface r2-eth1 + ip ospf network point-to-multipoint + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf prefix-suppress 10.1.2.2 +! +router ospf + ospf router-id 10.0.255.2 + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/v4_route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/v4_route.json new file mode 100644 index 0000000..870b106 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/v4_route.json @@ -0,0 +1,122 @@ +{ + "10.0.10.1\/32": [ + { + "prefix": "10.0.10.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.0.20.1\/32": [ + { + "prefix": "10.0.20.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "distance": 110, + "metric": 10, + "table": 254, + "internalStatus": 0, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.0.20.1\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.0.20.1\/32", + "prefixLen": 32, + "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 + } + ], + "10.0.30.0\/24": [ + { + "prefix": "10.0.30.0\/24", + "prefixLen": 24, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.1.2.2\/32": [ + { + "prefix": "10.1.2.2\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.1.2.2\/32", + "prefixLen": 32, + "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 + } + ] +} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/zebra.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/zebra.conf new file mode 100644 index 0000000..60ff53b --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/zebra.conf @@ -0,0 +1,7 @@ +! +interface r2-eth0 + ip address 10.0.20.1/32 +! +interface r2-eth1 + ip address 10.1.2.2/32 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospf-route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospf-route.json new file mode 100644 index 0000000..44ffe64 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospf-route.json @@ -0,0 +1 @@ +{"10.0.10.1\/32":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"},"10.0.20.1\/32":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"},"10.0.30.0\/24":{"routeType":"N","transit":false,"cost":10,"area":"0.0.0.0"}} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospfd.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospfd.conf new file mode 100644 index 0000000..579e273 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospfd.conf @@ -0,0 +1,11 @@ +! +interface r3-eth1 + ip ospf network point-to-multipoint + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf prefix-suppress 10.1.3.2 +! +router ospf + ospf router-id 10.0.255.3 + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/v4_route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/v4_route.json new file mode 100644 index 0000000..61d9555 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/v4_route.json @@ -0,0 +1,126 @@ +{ + "10.0.10.1\/32": [ + { + "prefix": "10.0.10.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.0.20.1\/32": [ + { + "prefix": "10.0.20.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.0.30.0\/24": [ + { + "prefix": "10.0.30.0\/24", + "prefixLen": 24, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "distance": 110, + "metric": 10, + "table": 254, + "internalStatus": 0, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.0.30.0\/24", + "prefixLen": 24, + "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 + } + ], + "10.0.30.1\/32": [ + { + "prefix": "10.0.30.1\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.1.3.2\/32": [ + { + "prefix": "10.1.3.2\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.1.3.2\/32", + "prefixLen": 32, + "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 + } + ] +} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/zebra.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/zebra.conf new file mode 100644 index 0000000..327bb8f --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/zebra.conf @@ -0,0 +1,7 @@ +! +interface r3-eth0 + ip address 10.0.30.1/24 +! +interface r3-eth1 + ip address 10.1.3.2/32 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/test_ospf_unnumbered_point_to_multipoint.py b/tests/topotests/ospf_unnumbered_point_to_multipoint/test_ospf_unnumbered_point_to_multipoint.py new file mode 100644 index 0000000..c0dd85c --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/test_ospf_unnumbered_point_to_multipoint.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_unnumbered_point_to_multipoint.py +# +# Copyright (c) 2024 by +# Vincent Jardin +# + +""" +test_ospf_unnumbered_point_to_multipoint.py: Test the OSPF unnumbered for routers with point to multipoint over Ethernet +""" + +import os +import sys +from functools import partial +import pytest +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.ospfd] + + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +def build_topo(tgen): + "Build function" + + # Create routers + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + # Create a empty network for router 1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + # Create a empty network for router 2 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + + # Create a empty network for router 3 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"]) + + # Interconect router 1, 2 and r3 to a common switch 4 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + + # The multicast packet delivery is somewhat controlled by + # the rp_filter. Setting it to '0' allows the kernel to pass + # up the mcast packet not destined for the local routers + # network. + topotest.sysctl_assure(tgen.net["r1"], "net.ipv4.conf.r1-eth1.rp_filter", 0) + topotest.sysctl_assure(tgen.net["r1"], "net.ipv4.conf.all.rp_filter", 0) + topotest.sysctl_assure(tgen.net["r2"], "net.ipv4.conf.r2-eth1.rp_filter", 0) + topotest.sysctl_assure(tgen.net["r2"], "net.ipv4.conf.all.rp_filter", 0) + topotest.sysctl_assure(tgen.net["r3"], "net.ipv4.conf.r3-eth1.rp_filter", 0) + topotest.sysctl_assure(tgen.net["r3"], "net.ipv4.conf.all.rp_filter", 0) + + # Initialize all routers. + tgen.start_router() + # tgen.mininet_cli() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_ospf_convergence(): + "Test OSPF daemon convergence and that we have received the ospf routes" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + for router, rnode in tgen.routers().items(): + logger.info('Waiting for router "%s" convergence', router) + + json_file = "{}/{}/ospf-route.json".format(CWD, router) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, rnode, "show ip ospf route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router) + assert result is None, assertmsg + # tgen.mininet_cli() + + +def test_ospf_kernel_route(): + "Test OSPF kernel route installation and we have the onlink success" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + rlist = tgen.routers().values() + for router in rlist: + logger.info('Checking OSPF IPv4 kernel routes in "%s"', router.name) + + json_file = "{}/{}/v4_route.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show ip route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"{}" JSON output mistmatches'.format(router) + assert result is None, assertmsg + # tgen.mininet_cli() + + +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/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py index 7a7ea85..49dd34d 100644 --- a/tests/topotests/ospfapi/test_ospf_clientapi.py +++ b/tests/topotests/ospfapi/test_ospf_clientapi.py @@ -277,7 +277,9 @@ def _test_add_data(tgen, apibin): "linkStateId": "230.0.0.2", "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "opaqueData": "00000202", + "opaqueValues": { + "opaqueData": "00000202" + } }, ], } @@ -327,7 +329,9 @@ def _test_add_data(tgen, apibin): "linkStateId": "231.0.0.1", "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "opaqueData": "00010101", + "opaqueValues": { + "opaqueData": "00010101", + } }, ], } @@ -376,7 +380,9 @@ def _test_add_data(tgen, apibin): "linkStateId": "232.0.0.3", "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "opaqueData": "deadbeaf01234567", + "opaqueValues": { + "opaqueData": "deadbeaf01234567", + } }, ] } @@ -427,7 +433,9 @@ def _test_add_data(tgen, apibin): "linkStateId": "232.0.0.3", "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000002", - "opaqueData": "ebadf00d", + "opaqueValues": { + "opaqueData": "ebadf00d", + } }, ] } @@ -574,7 +582,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "76bf", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "230.0.0.2", @@ -583,7 +591,7 @@ def _test_opaque_add_del(tgen, apibin): "checksum": "8aa2", "length": 24, "opaqueId": 2, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ] } @@ -599,7 +607,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5bd8", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "231.0.0.2", @@ -607,7 +615,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "7690", "length": 28, - "opaqueDataLength": 8, + "opaqueLength": 8, }, ], }, @@ -621,7 +629,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5ed5", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "232.0.0.2", @@ -629,7 +637,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "d9bd", "length": 24, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ], }, @@ -734,7 +742,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "76bf", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "230.0.0.2", @@ -744,7 +752,7 @@ def _test_opaque_add_del(tgen, apibin): "checksum": "8aa2", "length": 24, "opaqueId": 2, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ] } @@ -760,7 +768,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5bd8", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "lsaAge": 3600, @@ -770,7 +778,7 @@ def _test_opaque_add_del(tgen, apibin): "checksum": "4fe2", # data removed "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, ], }, @@ -785,7 +793,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5ed5", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "232.0.0.2", @@ -793,7 +801,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "d9bd", "length": 24, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ], }, @@ -827,7 +835,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "76bf", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "230.0.0.2", @@ -837,7 +845,7 @@ def _test_opaque_add_del(tgen, apibin): "checksum": "8aa2", "length": 24, "opaqueId": 2, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ] } @@ -854,7 +862,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5bd8", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "lsaAge": 3600, @@ -864,7 +872,7 @@ def _test_opaque_add_del(tgen, apibin): "checksum": "4fe2", # data removed "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, ], }, @@ -879,7 +887,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5ed5", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "232.0.0.2", @@ -888,7 +896,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "d9bd", "length": 24, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ], }, @@ -1044,7 +1052,7 @@ def _test_opaque_add_restart_add(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "b07a", "length": 28, - "opaqueDataLength": 8, + "opaqueLength": 8, }, ], }, @@ -1100,7 +1108,7 @@ def _test_opaque_add_restart_add(tgen, apibin): "lsaSeqNumber": "80000003", "checksum": "cb27", "length": 28, - "opaqueDataLength": 8, + "opaqueLength": 8, }, ], }, @@ -1205,7 +1213,10 @@ def _test_opaque_interface_disable(tgen, apibin): } } test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_without_opaque + topotest.router_json_cmp, + r1, + "show ip ospf interface json", + r1_interface_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" @@ -1232,7 +1243,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 4 in test_ospf_opaque_interface_disable and STEP 59 in CI tests step("Verify that the r1 neighbor options don't include opaque") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque + topotest.router_json_cmp, + r1, + "show ip ospf neighbor detail json", + r1_neighbor_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF neighbor has opaque option in optionsList" @@ -1241,7 +1255,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 5 in test_ospf_opaque_interface_disable and STEP 60 in CI tests step("Verify that the r1 neighbor options don't include opaque") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque + topotest.router_json_cmp, + r2, + "show ip ospf neighbor detail json", + r2_neighbor_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF neighbor has opaque option in optionsList" @@ -1282,7 +1299,10 @@ def _test_opaque_interface_disable(tgen, apibin): } } test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque + topotest.router_json_cmp, + r2, + "show ip ospf interface json", + r2_interface_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF interface has opaque capability disabled" @@ -1338,19 +1358,17 @@ def _test_opaque_interface_disable(tgen, apibin): "asExternalOpaqueLsaCount": 1, } opaque_area_empty_database = { - "routerId":"2.0.0.0", - "areaLocalOpaqueLsa":{ - "areas":{ - "1.2.3.4":[ - ] - } - } + "routerId": "2.0.0.0", + "areaLocalOpaqueLsa": {"areas": {"1.2.3.4": []}}, } # STEP 9 in test_ospf_opaque_interface_disable and STEP 64 in CI tests step("Check that LSAs are added on r1") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database + topotest.router_json_cmp, + r1, + "show ip ospf database json", + opaque_LSAs_in_database, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF database doesn't contain opaque LSAs" @@ -1359,8 +1377,11 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 10 in test_ospf_opaque_interface_disable and STEP 65 in CI tests step("Check that LSAs are not added on r2") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf database opaque-area json", - opaque_area_empty_database, True + topotest.router_json_cmp, + r2, + "show ip ospf database opaque-area json", + opaque_area_empty_database, + True, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF area database contains opaque LSAs" @@ -1382,7 +1403,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 13 in test_ospf_opaque_interface_disable and STEP 68 in CI tests step("Verify the ospf opaque option is applied to the r1 interface") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_with_opaque + topotest.router_json_cmp, + r1, + "show ip ospf interface json", + r1_interface_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" @@ -1409,7 +1433,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 14 in test_ospf_opaque_interface_disable and STEP 69 in CI tests step("Verify that the r1 neighbor options include opaque") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque + topotest.router_json_cmp, + r1, + "show ip ospf neighbor detail json", + r1_neighbor_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList" @@ -1418,7 +1445,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 15 in test_ospf_opaque_interface_disable and STEP 70 in CI tests step("Verify that the r2 neighbor options include opaque") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque + topotest.router_json_cmp, + r2, + "show ip ospf neighbor detail json", + r2_neighbor_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList" @@ -1427,7 +1457,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 16 in test_ospf_opaque_interface_disable and STEP 71 in CI tests step("Check that LSAs are now added to r2") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database + topotest.router_json_cmp, + r2, + "show ip ospf database json", + opaque_LSAs_in_database, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF database doesn't contains opaque LSAs" @@ -1463,7 +1496,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 20 in test_ospf_opaque_interface_disable and STEP 75 in CI tests step("Verify the ospf opaque option is not applied to the r2 interface") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_without_opaque + topotest.router_json_cmp, + r2, + "show ip ospf interface json", + r2_interface_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" @@ -1472,7 +1508,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 21 in test_ospf_opaque_interface_disable and STEP 76 in CI tests step("Verify that the r1 neighbor options don't include opaque") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque + topotest.router_json_cmp, + r1, + "show ip ospf neighbor detail json", + r1_neighbor_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF neighbor has opaque option in optionsList" @@ -1481,7 +1520,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 22 in test_ospf_opaque_interface_disable and STEP 77 in CI tests step("Verify that the r2 neighbor options don't include opaque") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque + topotest.router_json_cmp, + r2, + "show ip ospf neighbor detail json", + r2_neighbor_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF neighbor has opaque option in optionsList" @@ -1490,7 +1532,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 23 in test_ospf_opaque_interface_disable and STEP 78 in CI tests step("Verify that r1 still has the opaque LSAs") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database + topotest.router_json_cmp, + r1, + "show ip ospf database json", + opaque_LSAs_in_database, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF database doesn't contain opaque LSAs" @@ -1499,8 +1544,11 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 24 in test_ospf_opaque_interface_disable and STEP 79 in CI tests step("Verify that r2 doesn't have the opaque LSAs") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf database opaque-area json", - opaque_area_empty_database, True + topotest.router_json_cmp, + r2, + "show ip ospf database opaque-area json", + opaque_area_empty_database, + True, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF area database contains opaque LSAs" @@ -1524,7 +1572,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 27 in test_ospf_opaque_interface_disable and STEP 82 in CI tests step("Verify the ospf opaque option is applied to the r2 interface") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque + topotest.router_json_cmp, + r2, + "show ip ospf interface json", + r2_interface_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF interface doesn't have opaque capability disabled" @@ -1533,7 +1584,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 28 in test_ospf_opaque_interface_disable and STEP 83 in CI tests step("Verify that the r2 neighbor options include opaque") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque + topotest.router_json_cmp, + r2, + "show ip ospf neighbor detail json", + r2_neighbor_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList" @@ -1542,7 +1596,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 29 in test_ospf_opaque_interface_disable and STEP 84 in CI tests step("Verify that the r1 neighbor options include opaque") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque + topotest.router_json_cmp, + r1, + "show ip ospf neighbor detail json", + r1_neighbor_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList" @@ -1551,7 +1608,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 30 in test_ospf_opaque_interface_disable and STEP 85 in CLI tests step("Verify that r2 now has the opaque LSAs") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database + topotest.router_json_cmp, + r2, + "show ip ospf database json", + opaque_LSAs_in_database, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF database doesn't contain opaque LSAs" @@ -1581,6 +1641,93 @@ def test_ospf_opaque_interface_disable(tgen): _test_opaque_interface_disable(tgen, apibin) +def _test_opaque_link_local_lsa_crash(tgen, apibin): + "Test disabling opaque capability on an interface" + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + tc_name = "opaque_interface_disable" + + p = None + # Log to our stdin, stderr + pout = open(os.path.join(r1.net.logdir, "r1/intf-disable.log"), "a+") + try: + step("Add a link-local opaque LSA for r1-eth0") + pread = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,1,feedaceedeadbeef"]) + + input_dict = { + "linkLocalOpaqueLsa": { + "areas": { + "1.2.3.4": [ + { + "linkStateId": "230.0.0.1", + "advertisingRouter": "1.0.0.0", + "lsaSeqNumber": "80000001", + "opaqueValues": { + "opaqueData": "feedaceedeadbeef", + } + }, + ], + } + }, + } + + # verify content + json_cmd = "show ip ospf da opaque-link json" + assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None + + step("Shut down r1-eth0 and verify there is no crash") + r1.vtysh_multicmd("conf t\ninterface r1-eth0\nshut") + time.sleep(2) + + step("Bring r1-eth0 back up and verify there is no crash") + r1.vtysh_multicmd("conf t\ninterface r1-eth0\nno shut") + + step("Add another link-local opaque LSA for r1-eth0") + pread = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,1,feedaceecafebeef"]) + + input_dict = { + "linkLocalOpaqueLsa": { + "areas": { + "1.2.3.4": [ + { + "linkStateId": "230.0.0.1", + "advertisingRouter": "1.0.0.0", + "lsaSeqNumber": "80000001", + "opaqueValues": { + "opaqueData": "feedaceecafebeef", + } + }, + ], + } + }, + } + # verify content + json_cmd = "show ip ospf da opaque-link json" + assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None + + except Exception: + if p: + p.terminate() + if p.wait(): + comm_error(p) + p = None + raise + finally: + if p: + p.terminate() + p.wait() + p = None + + +@pytest.mark.parametrize("tgen", [2], indirect=True) +def test_ospf_opaque_link_local_lsa_crash(tgen): + apibin = os.path.join(CLIENTDIR, "ospfclient.py") + rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"]) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) + _test_opaque_link_local_lsa_crash(tgen, apibin) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/pbr_topo1/r1/pbr-map.json b/tests/topotests/pbr_topo1/r1/pbr-map.json index bfa0ecb..aaf2b5e 100644 --- a/tests/topotests/pbr_topo1/r1/pbr-map.json +++ b/tests/topotests/pbr_topo1/r1/pbr-map.json @@ -18,7 +18,7 @@ { "sequenceNumber":10, "vrfUnchanged":false, - "installed":true, + "installed":false, "installedReason":"Invalid Src or Dst", "nexthopGroup":{ "name":"C", @@ -98,7 +98,7 @@ { "sequenceNumber":5, "vrfUnchanged":false, - "installed":false, + "installed":true, "installedReason":"Invalid NH-group", "nexthopGroup":{ "name":"B", @@ -111,7 +111,7 @@ { "sequenceNumber":10, "vrfUnchanged":true, - "installed":false, + "installed":true, "installedReason":"Valid", "matchSrc":"1.2.0.0\/16", "matchDst":"3.4.5.0\/24" diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index 6c2d42e..98fcfbc 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -1,6 +1,8 @@ # Skip pytests example directory [pytest] +# NEEDS_EXABGP_4_2_11_FRR + # asyncio_mode = auto # We always turn this on inside conftest.py, default shown @@ -43,6 +45,7 @@ markers = eigrpd: Tests that run against EIGRPD isisd: Tests that run against ISISD ldpd: Tests that run against LDPD + mgmtd: Tests that run against MGMTD nhrpd: Tests that run against NHRPD ospf6d: Tests that run against OSPF6D ospfd: Tests that run against OSPFD diff --git a/tests/topotests/route_scale/r1/installed.routes.json b/tests/topotests/route_scale/r1/installed.routes.json index 25d209f..6e09f52 100644 --- a/tests/topotests/route_scale/r1/installed.routes.json +++ b/tests/topotests/route_scale/r1/installed.routes.json @@ -11,6 +11,6 @@ "type":"sharp" } ], - "routesTotal":1000032, - "routesTotalFib":1000032 + "routesTotal":1000064, + "routesTotalFib":1000064 } diff --git a/tests/topotests/route_scale/r1/no.routes.json b/tests/topotests/route_scale/r1/no.routes.json index abebd1b..13dc767 100644 --- a/tests/topotests/route_scale/r1/no.routes.json +++ b/tests/topotests/route_scale/r1/no.routes.json @@ -6,6 +6,6 @@ "type":"connected" } ], - "routesTotal":32, - "routesTotalFib":32 + "routesTotal":64, + "routesTotalFib":64 } diff --git a/tests/topotests/srv6_encap_src_addr/__init__.py b/tests/topotests/srv6_encap_src_addr/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/__init__.py diff --git a/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr.json b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr.json new file mode 100644 index 0000000..431027f --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr.json @@ -0,0 +1,9 @@ +{ + "parameters": { + "encapsulation":{ + "sourceAddress": { + "configured": "fc00:0:1::1" + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_set.json b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_set.json new file mode 100644 index 0000000..431027f --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_set.json @@ -0,0 +1,9 @@ +{ + "parameters": { + "encapsulation":{ + "sourceAddress": { + "configured": "fc00:0:1::1" + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_unset.json b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_unset.json new file mode 100644 index 0000000..18b317f --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_unset.json @@ -0,0 +1,9 @@ +{ + "parameters": { + "encapsulation":{ + "sourceAddress": { + "configured": "::" + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/srv6_encap_src_addr/r1/setup.sh b/tests/topotests/srv6_encap_src_addr/r1/setup.sh new file mode 100644 index 0000000..36ed713 --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/r1/setup.sh @@ -0,0 +1,2 @@ +ip link add dummy0 type dummy +ip link set dummy0 up diff --git a/tests/topotests/srv6_encap_src_addr/r1/zebra.conf b/tests/topotests/srv6_encap_src_addr/r1/zebra.conf new file mode 100644 index 0000000..c570756 --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/r1/zebra.conf @@ -0,0 +1,17 @@ +hostname r1 +! +! debug zebra events +! debug zebra rib detailed +! +log stdout notifications +log monitor notifications +log commands +log file zebra.log debugging +! +segment-routing + srv6 + encapsulation + source-address fc00:0:1::1 + ! + ! +! diff --git a/tests/topotests/srv6_encap_src_addr/test_srv6_encap_src_addr.py b/tests/topotests/srv6_encap_src_addr/test_srv6_encap_src_addr.py new file mode 100755 index 0000000..4239193 --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/test_srv6_encap_src_addr.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_srv6_encap_src_addr.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2022 by +# University of Rome Tor Vergata, Carmine Scarpitta <carmine.scarpitta@uniroma2.it> +# + +""" +test_srv6_encap_src_addr.py: +Test for SRv6 encap source address on zebra +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd, pytest.mark.sharpd] + + +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 setup_module(mod): + tgen = Topogen({None: "r1"}, mod.__name__) + tgen.start_topology() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + 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)) + ) + router.load_config( + TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) + ) + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_zebra_srv6_encap_src_addr(tgen): + "Test SRv6 encapsulation source address." + logger.info( + "Test SRv6 encapsulation source address." + ) + r1 = tgen.gears["r1"] + + # Generate expected results + json_file = "{}/r1/expected_srv6_encap_src_addr.json".format(CWD) + expected = json.loads(open(json_file).read()) + + ok = topotest.router_json_cmp_retry(r1, "show segment-routing srv6 manager json", expected) + assert ok, '"r1" JSON output mismatches' + + output = r1.cmd("ip sr tunsrc show") + assert output == "tunsrc addr fc00:0:1::1\n" + + +def test_zebra_srv6_encap_src_addr_unset(tgen): + "Test SRv6 encapsulation source address unset." + logger.info( + "Test SRv6 encapsulation source address unset." + ) + r1 = tgen.gears["r1"] + + # Unset SRv6 encapsulation source address + r1.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + encapsulation + no source-address + """ + ) + + # Generate expected results + json_file = "{}/r1/expected_srv6_encap_src_addr_unset.json".format(CWD) + expected = json.loads(open(json_file).read()) + + ok = topotest.router_json_cmp_retry(r1, "show segment-routing srv6 manager json", expected) + assert ok, '"r1" JSON output mismatches' + + output = r1.cmd("ip sr tunsrc show") + assert output == "tunsrc addr ::\n" + + +def test_zebra_srv6_encap_src_addr_set(tgen): + "Test SRv6 encapsulation source address set." + logger.info( + "Test SRv6 encapsulation source address set." + ) + r1 = tgen.gears["r1"] + + # Set SRv6 encapsulation source address + r1.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + encapsulation + source-address fc00:0:1::1 + """ + ) + + # Generate expected results + json_file = "{}/r1/expected_srv6_encap_src_addr_set.json".format(CWD) + expected = json.loads(open(json_file).read()) + + ok = topotest.router_json_cmp_retry(r1, "show segment-routing srv6 manager json", expected) + assert ok, '"r1" JSON output mismatches' + + output = r1.cmd("ip sr tunsrc show") + assert output == "tunsrc addr fc00:0:1::1\n" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/srv6_locator/r1/sharpd.conf b/tests/topotests/srv6_locator/r1/sharpd.conf index d460859..371e6f6 100644 --- a/tests/topotests/srv6_locator/r1/sharpd.conf +++ b/tests/topotests/srv6_locator/r1/sharpd.conf @@ -1,7 +1,6 @@ hostname r1 ! log stdout notifications -log monitor notifications log commands log file sharpd.log debugging ! diff --git a/tests/topotests/srv6_locator/r1/zebra.conf b/tests/topotests/srv6_locator/r1/zebra.conf index 85001d7..695cf0a 100644 --- a/tests/topotests/srv6_locator/r1/zebra.conf +++ b/tests/topotests/srv6_locator/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 ! debug zebra rib detailed ! log stdout notifications -log monitor notifications log commands log file zebra.log debugging ! diff --git a/tests/topotests/srv6_locator_custom_bits_length/r1/sharpd.conf b/tests/topotests/srv6_locator_custom_bits_length/r1/sharpd.conf index d460859..371e6f6 100644 --- a/tests/topotests/srv6_locator_custom_bits_length/r1/sharpd.conf +++ b/tests/topotests/srv6_locator_custom_bits_length/r1/sharpd.conf @@ -1,7 +1,6 @@ hostname r1 ! log stdout notifications -log monitor notifications log commands log file sharpd.log debugging ! diff --git a/tests/topotests/srv6_locator_custom_bits_length/r1/zebra.conf b/tests/topotests/srv6_locator_custom_bits_length/r1/zebra.conf index 30a520b..96033b6 100644 --- a/tests/topotests/srv6_locator_custom_bits_length/r1/zebra.conf +++ b/tests/topotests/srv6_locator_custom_bits_length/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 ! debug zebra rib detailed ! log stdout notifications -log monitor notifications log commands log file zebra.log debugging ! diff --git a/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator.py b/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator_custom_bits_length.py index 92980d3..92980d3 100755 --- a/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator.py +++ b/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator_custom_bits_length.py diff --git a/tests/topotests/srv6_locator_usid/r1/sharpd.conf b/tests/topotests/srv6_locator_usid/r1/sharpd.conf index d460859..371e6f6 100644 --- a/tests/topotests/srv6_locator_usid/r1/sharpd.conf +++ b/tests/topotests/srv6_locator_usid/r1/sharpd.conf @@ -1,7 +1,6 @@ hostname r1 ! log stdout notifications -log monitor notifications log commands log file sharpd.log debugging ! diff --git a/tests/topotests/srv6_locator_usid/r1/zebra.conf b/tests/topotests/srv6_locator_usid/r1/zebra.conf index 190e831..1a02e28 100644 --- a/tests/topotests/srv6_locator_usid/r1/zebra.conf +++ b/tests/topotests/srv6_locator_usid/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 ! debug zebra rib detailed ! log stdout notifications -log monitor notifications log commands log file zebra.log debugging ! diff --git a/tests/topotests/srv6_static_route/r1/staticd.conf b/tests/topotests/srv6_static_route/r1/staticd.conf index a75c69f..3c65a68 100644 --- a/tests/topotests/srv6_static_route/r1/staticd.conf +++ b/tests/topotests/srv6_static_route/r1/staticd.conf @@ -1,7 +1,6 @@ hostname r1 ! log stdout notifications -log monitor notifications log commands log file staticd.log debugging ! diff --git a/tests/topotests/srv6_static_route/r1/zebra.conf b/tests/topotests/srv6_static_route/r1/zebra.conf index cc70418..a596eeb 100644 --- a/tests/topotests/srv6_static_route/r1/zebra.conf +++ b/tests/topotests/srv6_static_route/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 ! debug zebra rib detailed ! log stdout notifications -log monitor notifications log commands log file zebra.log debugging ! diff --git a/tests/topotests/static_simple/r1/mgmtd.conf b/tests/topotests/static_simple/r1/mgmtd.conf index 0f9f97c..dd5761a 100644 --- a/tests/topotests/static_simple/r1/mgmtd.conf +++ b/tests/topotests/static_simple/r1/mgmtd.conf @@ -1 +1,11 @@ log timestamp precision 3 + +! way too noisy +! debug northbound libyang + +debug northbound notifications +debug northbound events +debug northbound callbacks +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend diff --git a/tests/topotests/static_simple/r1/zebra.conf b/tests/topotests/static_simple/r1/zebra.conf index ec82761..e3a4436 100644 --- a/tests/topotests/static_simple/r1/zebra.conf +++ b/tests/topotests/static_simple/r1/zebra.conf @@ -1,5 +1,15 @@ log timestamp precision 3 +! way too noisy +! debug northbound libyang + +debug northbound notifications +debug northbound events +debug northbound callbacks +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + interface r1-eth0 ip address 101.0.0.1/24 ipv6 address 2101::1/64 diff --git a/tests/topotests/static_simple/test_static_simple.py b/tests/topotests/static_simple/test_static_simple.py index fd87224..f862d81 100644 --- a/tests/topotests/static_simple/test_static_simple.py +++ b/tests/topotests/static_simple/test_static_simple.py @@ -40,6 +40,8 @@ def tgen(request): router.net.add_loop("lo-red") router.net.attach_iface_to_l3vrf("lo-red", "red") router.net.attach_iface_to_l3vrf(rname + "-eth1", "red") + # + # router.load_frr_config("frr.conf") # and select daemons to run router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") router.load_config(TopoRouter.RD_MGMTD) @@ -181,10 +183,11 @@ def guts(tgen, vrf, use_cli): r1 = tgen.routers()["r1"] - step("add via gateway", reset=True) - do_config(r1, 1, True, False, vrf=vrf, use_cli=use_cli) - step("remove via gateway") - do_config(r1, 1, False, False, vrf=vrf, use_cli=use_cli) + count = 10 + step(f"add {count} via gateway", reset=True) + do_config(r1, count, True, False, vrf=vrf, use_cli=use_cli) + step(f"remove {count} via gateway") + do_config(r1, count, False, False, vrf=vrf, use_cli=use_cli) via = f"lo-{vrf}" if vrf else "lo" step("add via loopback") diff --git a/tests/topotests/static_vrf/r1/frr.conf b/tests/topotests/static_vrf/r1/frr.conf new file mode 100644 index 0000000..bb373b9 --- /dev/null +++ b/tests/topotests/static_vrf/r1/frr.conf @@ -0,0 +1,18 @@ +interface r1-eth0 vrf red + ip address 192.0.2.1/23 +exit + +interface r1-eth1 vrf blue + ip address 192.0.2.129/24 +exit + +ip route 198.51.100.1/32 192.0.2.2 nexthop-vrf red +ip route 198.51.100.1/32 192.0.2.130 nexthop-vrf blue +ip route 198.51.100.2/32 r1-eth0 nexthop-vrf red +ip route 198.51.100.2/32 r1-eth1 nexthop-vrf blue + +ip route 203.0.113.1/32 192.0.2.130 vrf red nexthop-vrf blue +ip route 203.0.113.2/32 r1-eth1 vrf red nexthop-vrf blue + +ip route 203.0.113.129/32 192.0.2.2 vrf blue nexthop-vrf red +ip route 203.0.113.130/32 r1-eth0 vrf blue nexthop-vrf red diff --git a/tests/topotests/static_vrf/test_static_vrf.py b/tests/topotests/static_vrf/test_static_vrf.py new file mode 100644 index 0000000..97c0800 --- /dev/null +++ b/tests/topotests/static_vrf/test_static_vrf.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2024 NFWare Inc. +# +# noqa: E501 +# +""" +Test static route functionality +""" + +import ipaddress + +import pytest +from lib.topogen import Topogen +from lib.common_config import retry + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.attach_iface_to_l3vrf(rname + "-eth0", "red") + # Setup VRF blue + router.net.add_l3vrf("blue", 20) + router.net.attach_iface_to_l3vrf(rname + "-eth1", "blue") + # Load configuration + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +@retry(retry_timeout=1, initial_wait=0.1) +def check_kernel(r1, prefix, nexthops, vrf, expected_p=True, expected_nh=True): + vrfstr = f" vrf {vrf}" if vrf else "" + + net = ipaddress.ip_network(prefix) + if net.version == 6: + kernel = r1.run(f"ip -6 route show{vrfstr} {prefix}") + else: + kernel = r1.run(f"ip -4 route show{vrfstr} {prefix}") + + if expected_p: + assert prefix in kernel, f"Failed to find \n'{prefix}'\n in \n'{kernel:.1920}'" + else: + assert ( + prefix not in kernel + ), f"Failed found \n'{prefix}'\n in \n'{kernel:.1920}'" + + if not expected_p: + return + + for nh in nexthops: + if expected_nh: + assert f"{nh}" in kernel, f"Failed to find \n'{nh}'\n in \n'{kernel:.1920}'" + else: + assert ( + f"{nh}" not in kernel + ), f"Failed found \n'{nh}'\n in \n'{kernel:.1920}'" + + +def test_static_vrf(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Check initial configuration + check_kernel(r1, "198.51.100.1", ["192.0.2.2", "192.0.2.130"], None) + check_kernel(r1, "198.51.100.2", ["r1-eth0", "r1-eth1"], None) + check_kernel(r1, "203.0.113.1", ["192.0.2.130"], "red") + check_kernel(r1, "203.0.113.2", ["r1-eth1"], "red") + check_kernel(r1, "203.0.113.129", ["192.0.2.2"], "blue") + check_kernel(r1, "203.0.113.130", ["r1-eth0"], "blue") + + # Delete VRF red + r1.net.del_iface("red") + + # Check that "red" nexthops are removed, "blue" nexthops are still there + check_kernel(r1, "198.51.100.1", ["192.0.2.2"], None, expected_nh=False) + check_kernel(r1, "198.51.100.1", ["192.0.2.130"], None) + check_kernel(r1, "198.51.100.2", ["r1-eth0"], None, expected_nh=False) + check_kernel(r1, "198.51.100.2", ["r1-eth1"], None) + check_kernel(r1, "203.0.113.129", ["192.0.2.2"], "blue", expected_p=False) + check_kernel(r1, "203.0.113.130", ["r1-eth0"], "blue", expected_p=False) + + # Delete VRF blue + r1.net.del_iface("blue") + + # Check that "blue" nexthops are removed + check_kernel(r1, "198.51.100.1", ["192.0.2.130"], None, expected_p=False) + check_kernel(r1, "198.51.100.2", ["r1-eth1"], None, expected_p=False) + + # Add VRF red back, attach "eth0" to it + r1.net.add_l3vrf("red", 10) + r1.net.attach_iface_to_l3vrf("r1-eth0", "red") + + # Check that "red" nexthops are restored + check_kernel(r1, "198.51.100.1", ["192.0.2.2"], None) + check_kernel(r1, "198.51.100.2", ["r1-eth0"], None) + + # Add VRF blue back, attach "eth1" to it + r1.net.add_l3vrf("blue", 20) + r1.net.attach_iface_to_l3vrf("r1-eth1", "blue") + + # Check that everything is restored + check_kernel(r1, "198.51.100.1", ["192.0.2.2", "192.0.2.130"], None) + check_kernel(r1, "198.51.100.2", ["r1-eth0", "r1-eth1"], None) + check_kernel(r1, "203.0.113.1", ["192.0.2.130"], "red") + check_kernel(r1, "203.0.113.2", ["r1-eth1"], "red") + check_kernel(r1, "203.0.113.129", ["192.0.2.2"], "blue") + check_kernel(r1, "203.0.113.130", ["r1-eth0"], "blue") diff --git a/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py b/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py index 529520c..0b2937c 100644 --- a/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py +++ b/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py @@ -144,6 +144,23 @@ def test_zebra_system_recursion(): assert result is None, "Kernel route is missing from zebra" +def test_zebra_noprefix_connected(): + "Test that a noprefixroute created does not create a connected route" + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + router.run("ip addr add 192.168.44.1/24 dev r1-eth1 noprefixroute") + expected = "% Network not in table" + test_func = partial( + topotest.router_output_cmp, router, "show ip route 192.168.44.0/24", expected + ) + result, diff = topotest.run_and_expect(test_func, "", count=20, wait=1) + assert result, "Connected Route should not have been added" + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/zebra_seg6_route/r1/zebra.conf b/tests/topotests/zebra_seg6_route/r1/zebra.conf index e5e360f..2d0c9e1 100644 --- a/tests/topotests/zebra_seg6_route/r1/zebra.conf +++ b/tests/topotests/zebra_seg6_route/r1/zebra.conf @@ -1,7 +1,6 @@ log file zebra.log ! log stdout notifications -log monitor notifications log commands ! ! debug zebra packet diff --git a/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py b/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py index 4b462a5..f872f7a 100755 --- a/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py +++ b/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py @@ -59,11 +59,11 @@ def teardown_module(_mod): tgen.stop_topology() -def test_zebra_seg6local_routes(): +def test_zebra_seg6_routes(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info("Test for seg6local route install via ZAPI was start.") + logger.info("Test for seg6 route install via ZAPI was start.") r1 = tgen.gears["r1"] def check(router, dest, expected): diff --git a/tests/topotests/zebra_seg6local_route/r1/zebra.conf b/tests/topotests/zebra_seg6local_route/r1/zebra.conf index dee7a91..a3b9335 100644 --- a/tests/topotests/zebra_seg6local_route/r1/zebra.conf +++ b/tests/topotests/zebra_seg6local_route/r1/zebra.conf @@ -1,7 +1,6 @@ log file zebra.log ! log stdout notifications -log monitor notifications log commands ! ! debug zebra packet |