From 763069cd3b31e8e469b2cf98c3d808d2187da929 Mon Sep 17 00:00:00 2001 From: Martin Buck Date: Fri, 20 Dec 2024 18:54:58 +0100 Subject: [PATCH 1/2] tests: ospf6_ecmp_inter_area, use router_json_cmp Use router_json_cmp when checking for proper nexthops to get better diagnostics in case of mismatches. Add step() messages to see which check failed. Based on original PR#16811 commit: expect_num_nexthops() errors are not understandable. Use router_json_cmp. Signed-off-by: Louis Scalbert Signed-off-by: Martin Buck --- .../r1/show_ipv6_routes_ospf6-1.json | 155 ++++++++++++++++++ .../r1/show_ipv6_routes_ospf6-2.json | 130 +++++++++++++++ .../r1/show_ipv6_routes_ospf6-3.json | 110 +++++++++++++ .../test_ospf6_ecmp_inter_area.py | 101 ++++++------ 4 files changed, 448 insertions(+), 48 deletions(-) create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-1.json create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-2.json create mode 100644 tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-3.json diff --git a/tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-1.json b/tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-1.json new file mode 100644 index 000000000000..ff2cf3119373 --- /dev/null +++ b/tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-1.json @@ -0,0 +1,155 @@ +{ + "2001:db8:2::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "2001:db8:3::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ], + "2001:db8:4::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + } + ] + } + ], + "2001:db8:5::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "2001:db8:6::/64": [ + { + "internalNextHopActiveNum": 2, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ], + "2001:db8:7::/64": [ + { + "internalNextHopActiveNum": 3, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ], + "2001:db8:8::/64": [ + { + "internalNextHopActiveNum": 3, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ], + "2001:db8:8007::/64": [ + { + "internalNextHopActiveNum": 3, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ], + "2001:db8:8008::/64": [ + { + "internalNextHopActiveNum": 3, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-2.json b/tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-2.json new file mode 100644 index 000000000000..8918feb969d3 --- /dev/null +++ b/tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-2.json @@ -0,0 +1,130 @@ +{ + "2001:db8:2::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "2001:db8:3::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ], + "2001:db8:4::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + } + ] + } + ], + "2001:db8:5::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "2001:db8:6::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + } + ] + } + ], + "2001:db8:7::/64": [ + { + "internalNextHopActiveNum": 2, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "2001:db8:8::/64": [ + { + "internalNextHopActiveNum": 2, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "2001:db8:8007::/64": [ + { + "internalNextHopActiveNum": 2, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "2001:db8:8008::/64": [ + { + "internalNextHopActiveNum": 2, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + }, + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-3.json b/tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-3.json new file mode 100644 index 000000000000..99ceb036d598 --- /dev/null +++ b/tests/topotests/ospf6_ecmp_inter_area/r1/show_ipv6_routes_ospf6-3.json @@ -0,0 +1,110 @@ +{ + "2001:db8:2::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "2001:db8:3::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ], + "2001:db8:4::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + } + ] + } + ], + "2001:db8:5::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + } + ] + } + ], + "2001:db8:6::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + } + ] + } + ], + "2001:db8:7::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + } + ] + } + ], + "2001:db8:8::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + } + ] + } + ], + "2001:db8:8007::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + } + ] + } + ], + "2001:db8:8008::/64": [ + { + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth2", + "active": true + } + ] + } + ] +} 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 adf289e2de20..6f1dd6a54a8b 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 @@ -43,12 +43,13 @@ With all links up, we expect 3 ECMP paths and 3 nexthops on R1 towards each of R7/8. Then we bring down the R3-R6 link, causing only 2 remaining paths and 2 nexthops on R1. Then we bring down the R2-R5 link, causing only -1 remaining path and 1 nexthop on R1. +1 remaining path and 1 nexthop on R1. -The test is successful if the number of nexthops for the routes on R1 is as -expected. +The test is successful if the number of nexthops and their interfaces for +the routes on R1 is as expected. """ +import json import os import sys from functools import partial @@ -62,7 +63,7 @@ # Import topogen and topotest helpers from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen -from lib.topolog import logger +from lib.common_config import write_test_header, write_test_footer, step # Required to instantiate the topology builder class. @@ -111,17 +112,42 @@ def setup_module(mod): tgen.start_router() -def test_wait_protocol_convergence(): +def expect_routes_json(router, exp_routes_json_fname, stepmsg): + "Wait until OSPFv3 routes match JSON spec" + step( + "waiting for OSPFv3 router '{}' routes/nexthops to match {} ({})".format( + router, exp_routes_json_fname, stepmsg + ) + ) + + json_file = "{}/{}/{}".format(CWD, router, exp_routes_json_fname) + expected = json.loads(open(json_file).read()) + tgen = get_topogen() + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ipv6 route ospf6 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assertmsg = '"{}" JSON output mismatches ({})'.format(router, stepmsg) + assert result is None, assertmsg + + +def test_wait_protocol_convergence(request): "Wait for OSPFv3 to converge" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info("waiting for protocols to converge") + step("waiting for protocols to converge") def expect_neighbor_full(router, neighbor): "Wait until OSPFv3 neighborship is full" - logger.info( + step( "waiting for OSPFv3 router '{}' neighborship with '{}'".format( router, neighbor ) @@ -156,57 +182,31 @@ def expect_neighbor_full(router, neighbor): expect_neighbor_full("r8", "10.254.254.5") expect_neighbor_full("r8", "10.254.254.6") + expect_routes_json("r1", "show_ipv6_routes_ospf6-1.json", "post-convergence") -def test_ecmp_inter_area(): + write_test_footer(tc_name) + + +def test_ecmp_inter_area(request): "Test whether OSPFv3 ECMP nexthops are properly updated for inter-area routes after link down" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - def num_nexthops(router): - # Careful: "show ipv6 ospf6 route json" doesn't work here. It will - # only list one route type per prefix and that might not necessarily - # be the best/selected route. "show ipv6 route ospf6 json" only - # lists selected routes, so that's more useful in this case. - routes = tgen.gears[router].vtysh_cmd("show ipv6 route ospf6 json", isjson=True) - route_prefixes_infos = sorted(routes.items()) - # Note: ri may contain one entry per routing protocol, but since - # we've explicitly requested only ospf6 above, we can count on ri[0] - # being the entry we're looking for. - return [ri[0]["internalNextHopActiveNum"] for rp, ri in route_prefixes_infos] - - 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 - ) - ) - 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) - - # Check nexthops pre link-down - # tgen.mininet_cli() - expect_num_nexthops("r1", [1, 1, 1, 1, 2, 3, 3, 3, 3], 4) - - logger.info("triggering R3-R6 link down") + step("triggering R3-R6 link down") tgen.gears["r3"].run("ip link set r3-eth1 down") - # tgen.mininet_cli() - # Check nexthops post link-down - expect_num_nexthops("r1", [1, 1, 1, 1, 1, 2, 2, 2, 2], 8) + expect_routes_json("r1", "show_ipv6_routes_ospf6-2.json", "post-R3-R6-link-down") - logger.info("triggering R2-R5 link down") + step("triggering R2-R5 link down") tgen.gears["r2"].run("ip link set r2-eth1 down") - # tgen.mininet_cli() - # Check nexthops post link-down - expect_num_nexthops("r1", [1, 1, 1, 1, 1, 1, 1, 1, 1], 8) + expect_routes_json("r1", "show_ipv6_routes_ospf6-3.json", "post-R2-R5-link-down") + + write_test_footer(tc_name) def teardown_module(_mod): @@ -215,14 +215,19 @@ def teardown_module(_mod): tgen.stop_topology() -def test_memory_leak(): +def test_memory_leak(request): "Run the memory leak test and report results." + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() if not tgen.is_memleak_enabled(): pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() + write_test_footer(tc_name) + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] From a09d98a95e23732fe9512a768f0fd97658a6cddf Mon Sep 17 00:00:00 2001 From: Martin Buck Date: Fri, 20 Dec 2024 18:55:26 +0100 Subject: [PATCH 2/2] tests: ospf6_ecmp_inter_area, no shutdown r7/r8 eth3 Drop eth3 shutdown from ospf6d.conf - it doesn't do anything there. And it actually shouldn't do anything: eth3 on r7/r8 are used as loopback-like interfaces to inject the address on eth2 into OSPFv3. So they need to be up for eth2 to work as expected. Based on original PR#16811 commit: eth3 shutdown is not applied because it is ospf6d.conf. Signed-off-by: Louis Scalbert Signed-off-by: Martin Buck --- tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf | 3 --- tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf | 3 --- 2 files changed, 6 deletions(-) diff --git a/tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf index 9b7756e838c1..451cf2f728f8 100644 --- a/tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf +++ b/tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf @@ -13,9 +13,6 @@ interface r7-eth2 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! -interface r7-eth3 - shutdown -! router ospf6 ospf6 router-id 10.254.254.7 redistribute connected diff --git a/tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf index 33c64979ca6f..f8d8619bc2e4 100644 --- a/tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf +++ b/tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf @@ -13,9 +13,6 @@ interface r8-eth2 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! -interface r8-eth3 - shutdown -! router ospf6 ospf6 router-id 10.254.254.8 redistribute connected