diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /tests/topotests/ospf6_topo2/test_ospf6_topo2.py | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/topotests/ospf6_topo2/test_ospf6_topo2.py')
-rw-r--r-- | tests/topotests/ospf6_topo2/test_ospf6_topo2.py | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py new file mode 100644 index 0000000..f95f7bb --- /dev/null +++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py @@ -0,0 +1,653 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf6_topo2.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_ospf6_topo2.py: Test the FRR OSPFv3 daemon. +""" + +import os +import sys +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.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.ospf6d] + + +def expect_lsas(router, area, lsas, wait=5, extra_params=""): + """ + Run the OSPFv3 show LSA database command and expect the supplied LSAs. + + Optional parameters: + * `wait`: amount of seconds to wait. + * `extra_params`: extra LSA database parameters. + * `inverse`: assert the inverse of the expected. + """ + tgen = get_topogen() + + command = "show ipv6 ospf6 database {} json".format(extra_params) + + logger.info("waiting OSPFv3 router '{}' LSA".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + command, + {"areaScopedLinkStateDb": [{"areaId": area, "lsa": lsas}]}, + ) + _, result = topotest.run_and_expect(test_func, None, count=wait, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + + assert result is None, assertmsg + + +def expect_ospfv3_routes(router, routes, wait=5, type=None, detail=False): + "Run command `ipv6 ospf6 route` and expect route with type." + tgen = get_topogen() + + if detail == False: + if type == None: + cmd = "show ipv6 ospf6 route json" + else: + cmd = "show ipv6 ospf6 route {} json".format(type) + else: + if type == None: + cmd = "show ipv6 ospf6 route detail json" + else: + cmd = "show ipv6 ospf6 route {} detail json".format(type) + + logger.info("waiting OSPFv3 router '{}' route".format(router)) + test_func = partial( + topotest.router_json_cmp, tgen.gears[router], cmd, {"routes": routes} + ) + _, result = topotest.run_and_expect(test_func, None, count=wait, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + + assert result is None, assertmsg + + +def dont_expect_route(router, unexpected_route, type=None): + "Specialized test function to expect route go missing" + tgen = get_topogen() + + if type == None: + cmd = "show ipv6 ospf6 route json" + else: + cmd = "show ipv6 ospf6 route {} json".format(type) + + output = tgen.gears[router].vtysh_cmd(cmd, isjson=True) + if unexpected_route in output["routes"]: + return output["routes"][unexpected_route] + return None + + +def build_topo(tgen): + "Build function" + + # Create 4 routers + 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"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r4"], nodeif="r4-stubnet") + + +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(): + + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + daemon_file = "{}/{}/ospf6d.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_OSPF6, daemon_file) + + # Initialize all routers. + tgen.start_router() + + +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.254.254.2") + expect_neighbor_full("r2", "10.254.254.1") + expect_neighbor_full("r2", "10.254.254.3") + expect_neighbor_full("r2", "10.254.254.4") + expect_neighbor_full("r3", "10.254.254.2") + expect_neighbor_full("r4", "10.254.254.2") + + +def test_ospfv3_expected_route_types(): + "Test routers route type to determine if NSSA/Stub is working as expected." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_ospf6_route_types(router, expected_summary): + "Expect the correct route types." + logger.info("waiting OSPFv3 router '{}'".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ipv6 ospf6 route summary json", + expected_summary, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + # Stub router: no external routes. + expect_ospf6_route_types( + "r1", + { + "numberOfIntraAreaRoutes": 1, + "numberOfInterAreaRoutes": 3, + "numberOfExternal1Routes": 0, + "numberOfExternal2Routes": 0, + }, + ) + # NSSA router: no external routes. + expect_ospf6_route_types( + "r4", + { + "numberOfIntraAreaRoutes": 1, + "numberOfInterAreaRoutes": 2, + "numberOfExternal1Routes": 0, + "numberOfExternal2Routes": 3, + }, + ) + + +def test_ospf6_default_route(): + "Wait for OSPFv3 default route in stub area." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for default route") + + def expect_route(router, route, metric): + "Test OSPF6 route existence." + logger.info("waiting OSPFv3 router '{}' routes".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ipv6 route json", + {route: [{"metric": metric}]}, + ) + _, result = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + metric = 123 + expect_lsas( + "r1", + "0.0.0.1", + [{"prefix": "::/0", "metric": metric}], + extra_params="inter-prefix detail", + ) + expect_route("r1", "::/0", metric + 10) + + +def test_redistribute_metrics(): + """ + Test that the configured metrics are honored when a static route is + redistributed. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Add new static route on r3. + config = """ + configure terminal + ipv6 route 2001:db8:500::/64 Null0 + """ + tgen.gears["r3"].vtysh_cmd(config) + + route = { + "2001:db8:500::/64": { + "metricType": 2, + "metricCost": 10, + } + } + logger.info( + "Expecting AS-external route 2001:db8:500::/64 to show up with default metrics" + ) + expect_ospfv3_routes("r2", route, wait=30, detail=True) + + # Change the metric of redistributed routes of the static type on r3. + config = """ + configure terminal + router ospf6 + redistribute static metric 50 metric-type 1 + """ + tgen.gears["r3"].vtysh_cmd(config) + + # Check if r3 reinstalled 2001:db8:500::/64 using the new metric type and value. + route = { + "2001:db8:500::/64": { + "metricType": 1, + "metricCost": 60, + } + } + logger.info( + "Expecting AS-external route 2001:db8:500::/64 to show up with updated metric type and value" + ) + expect_ospfv3_routes("r2", route, wait=30, detail=True) + + +def test_nssa_lsa_type7(): + """ + Test that static route gets announced as external route when redistributed + and gets removed when redistribution stops. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # + # Add new static route and check if it gets announced as LSA Type-7. + # + config = """ + configure terminal + ipv6 route 2001:db8:100::/64 Null0 + """ + tgen.gears["r2"].vtysh_cmd(config) + + lsas = [ + { + "type": "NSSA", + "advertisingRouter": "10.254.254.2", + "prefix": "2001:db8:100::/64", + "forwardingAddress": "2001:db8:3::1", + } + ] + route = { + "2001:db8:100::/64": { + "pathType": "E2", + "nextHops": [{"nextHop": "::", "interfaceName": "r4-eth0"}], + } + } + + logger.info("Expecting LSA type-7 and OSPFv3 route 2001:db8:100::/64 to show up") + expect_lsas("r4", "0.0.0.2", lsas, wait=30, extra_params="type-7 detail") + expect_ospfv3_routes("r4", route, wait=30) + + # + # Remove static route and check for LSA Type-7 removal. + # + config = """ + configure terminal + no ipv6 route 2001:db8:100::/64 Null0 + """ + tgen.gears["r2"].vtysh_cmd(config) + + def dont_expect_lsa(unexpected_lsa): + "Specialized test function to expect LSA go missing" + output = tgen.gears["r4"].vtysh_cmd( + "show ipv6 ospf6 database type-7 detail json", isjson=True + ) + for lsa in output["areaScopedLinkStateDb"][0]["lsa"]: + if lsa["prefix"] == unexpected_lsa["prefix"]: + if lsa["forwardingAddress"] == unexpected_lsa["forwardingAddress"]: + return lsa + return None + + logger.info("Expecting LSA type-7 and OSPFv3 route 2001:db8:100::/64 to go away") + + # Test that LSA doesn't exist. + test_func = partial(dont_expect_lsa, lsas[0]) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" LSA still exists'.format("r4") + assert result is None, assertmsg + + # Test that route doesn't exist. + test_func = partial(dont_expect_route, "r4", "2001:db8:100::/64") + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" route still exists'.format("r4") + assert result is None, assertmsg + + +def test_nssa_no_summary(): + """ + Test the following: + * Type-3 inter-area routes should be removed when the NSSA no-summary option + is configured; + * A type-3 inter-area default route should be originated into the NSSA area + when the no-summary option is configured; + * Once the no-summary option is unconfigured, all previously existing + Type-3 inter-area routes should be re-added, and the inter-area default + route removed. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # + # Configure area 1 as a NSSA totally stub area. + # + config = """ + configure terminal + router ospf6 + area 2 nssa no-summary + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting inter-area routes to be removed") + for route in ["2001:db8:1::/64", "2001:db8:2::/64"]: + test_func = partial(dont_expect_route, "r4", route, type="inter-area") + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = "{}'s {} inter-area route still exists".format("r4", route) + assert result is None, assertmsg + + logger.info("Expecting inter-area default-route to be added") + routes = {"::/0": {}} + expect_ospfv3_routes("r4", routes, wait=30, type="inter-area") + + # + # Configure area 1 as a regular NSSA area. + # + config = """ + configure terminal + router ospf6 + area 2 nssa + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting inter-area routes to be re-added") + routes = {"2001:db8:1::/64": {}, "2001:db8:2::/64": {}} + expect_ospfv3_routes("r4", routes, wait=30, type="inter-area") + + logger.info("Expecting inter-area default route to be removed") + test_func = partial(dont_expect_route, "r4", "::/0", type="inter-area") + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = "{}'s inter-area default route still exists".format("r4") + assert result is None, assertmsg + + +def test_nssa_default_originate(): + """ + Test the following: + * A type-7 default route should be originated into the NSSA area + when the default-information-originate option is configured; + * Once the default-information-originate option is unconfigured, the + previously originated Type-7 default route should be removed. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # + # Configure r2 to announce a Type-7 default route. + # + config = """ + configure terminal + router ospf6 + no default-information originate + area 2 nssa default-information-originate + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting Type-7 default-route to be added") + routes = {"::/0": {}} + expect_ospfv3_routes("r4", routes, wait=30, type="external-2") + + # + # Configure r2 to stop announcing a Type-7 default route. + # + config = """ + configure terminal + router ospf6 + area 2 nssa + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting Type-7 default route to be removed") + test_func = partial(dont_expect_route, "r4", "::/0", type="external-2") + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "r4's Type-7 default route still exists" + assert result is None, assertmsg + + +def test_area_filters(): + """ + Test ABR import/export filters. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # + # Configure import/export filters on r2 (ABR for area 2). + # + config = """ + configure terminal + ipv6 access-list ACL_IMPORT seq 5 permit 2001:db8:2::/64 + ipv6 access-list ACL_IMPORT seq 10 deny any + ipv6 access-list ACL_EXPORT seq 10 deny any + router ospf6 + area 1 import-list ACL_IMPORT + area 1 export-list ACL_EXPORT + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting inter-area routes to be removed on r1") + for route in ["::/0", "2001:db8:3::/64"]: + test_func = partial(dont_expect_route, "r1", route, type="inter-area") + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = "{}'s {} inter-area route still exists".format("r1", route) + assert result is None, assertmsg + + logger.info("Expecting inter-area routes to be removed on r3") + for route in ["2001:db8:1::/64"]: + test_func = partial(dont_expect_route, "r3", route, type="inter-area") + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = "{}'s {} inter-area route still exists".format("r3", route) + assert result is None, assertmsg + + # + # Update the ACLs used by the import/export filters. + # + config = """ + configure terminal + ipv6 access-list ACL_IMPORT seq 6 permit 2001:db8:3::/64 + ipv6 access-list ACL_EXPORT seq 5 permit 2001:db8:1::/64 + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting 2001:db8:3::/64 to be re-added on r1") + routes = {"2001:db8:3::/64": {}} + expect_ospfv3_routes("r1", routes, wait=30, type="inter-area") + logger.info("Expecting 2001:db8:1::/64 to be re-added on r3") + routes = {"2001:db8:1::/64": {}} + expect_ospfv3_routes("r3", routes, wait=30, type="inter-area") + + # + # Unconfigure r2's ABR import/export filters. + # + config = """ + configure terminal + router ospf6 + no area 1 import-list ACL_IMPORT + no area 1 export-list ACL_EXPORT + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting ::/0 to be re-added on r1") + routes = {"::/0": {}} + expect_ospfv3_routes("r1", routes, wait=30, type="inter-area") + + +def test_nssa_range(): + """ + Test NSSA ABR ranges. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Configure new addresses on r4 and enable redistribution of connected + # routes. + config = """ + configure terminal + interface r4-stubnet + ipv6 address 2001:db8:1000::1/128 + ipv6 address 2001:db8:1000::2/128 + router ospf6 + redistribute connected + """ + tgen.gears["r4"].vtysh_cmd(config) + logger.info("Expecting NSSA-translated external routes to be added on r3") + routes = {"2001:db8:1000::1/128": {}, "2001:db8:1000::2/128": {}} + expect_ospfv3_routes("r3", routes, wait=30, type="external-2") + + # Configure an NSSA range on r2 (ABR for area 2). + config = """ + configure terminal + router ospf6 + area 2 nssa range 2001:db8:1000::/64 + """ + tgen.gears["r2"].vtysh_cmd(config) + logger.info("Expecting summarized routes to be removed from r3") + for route in ["2001:db8:1000::1/128", "2001:db8:1000::2/128"]: + test_func = partial(dont_expect_route, "r3", route, type="external-2") + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}'s {} summarized route still exists".format("r3", route) + assert result is None, assertmsg + logger.info("Expecting NSSA range to be added on r3") + routes = { + "2001:db8:1000::/64": { + "metricType": 2, + "metricCost": 20, + "metricCostE2": 10, + } + } + expect_ospfv3_routes("r3", routes, wait=30, type="external-2", detail=True) + + # Change the NSSA range cost. + config = """ + configure terminal + router ospf6 + area 2 nssa range 2001:db8:1000::/64 cost 1000 + """ + tgen.gears["r2"].vtysh_cmd(config) + logger.info("Expecting NSSA range to be updated with new cost") + routes = { + "2001:db8:1000::/64": { + "metricType": 2, + "metricCost": 20, + "metricCostE2": 1000, + } + } + expect_ospfv3_routes("r3", routes, wait=30, type="external-2", detail=True) + + # Configure the NSSA range to not be advertised. + config = """ + configure terminal + router ospf6 + area 2 nssa range 2001:db8:1000::/64 not-advertise + """ + tgen.gears["r2"].vtysh_cmd(config) + logger.info("Expecting NSSA summary route to be removed") + route = "2001:db8:1000::/64" + test_func = partial(dont_expect_route, "r3", route, type="external-2") + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}'s {} NSSA summary route still exists".format("r3", route) + assert result is None, assertmsg + + # Remove the NSSA range. + config = """ + configure terminal + router ospf6 + no area 2 nssa range 2001:db8:1000::/64 + """ + tgen.gears["r2"].vtysh_cmd(config) + logger.info("Expecting previously summarized routes to be re-added") + routes = { + "2001:db8:1000::1/128": { + "metricType": 2, + "metricCostE2": 20, + }, + "2001:db8:1000::2/128": { + "metricType": 2, + "metricCostE2": 20, + }, + } + expect_ospfv3_routes("r3", routes, wait=30, type="external-2", detail=True) + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +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)) |