diff options
| author | Louis Scalbert <louis.scalbert@6wind.com> | 2025-03-19 15:51:30 +0100 | 
|---|---|---|
| committer | Louis Scalbert <louis.scalbert@6wind.com> | 2025-03-21 10:23:16 +0100 | 
| commit | 49567328b9f0bd57becaf607f3c235c0b655416a (patch) | |
| tree | d1b617e880e69dc0ab3c628e5b1e555f11498531 /tests | |
| parent | 361f80a64b69640dbbf472e77d6d04d2e62f409a (diff) | |
tests: add bfd_static_vrf
Add bfd_static_vrf to test BFD tracking of static routes in VRF.
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/topotests/bfd_static_vrf/__init__.py | 0 | ||||
| -rw-r--r-- | tests/topotests/bfd_static_vrf/r1/frr.conf | 23 | ||||
| -rw-r--r-- | tests/topotests/bfd_static_vrf/r1/show_bfd_peers.json | 90 | ||||
| -rw-r--r-- | tests/topotests/bfd_static_vrf/r1/show_bfd_peers_step1.json | 90 | ||||
| -rw-r--r-- | tests/topotests/bfd_static_vrf/r1/show_ip_route.json | 55 | ||||
| -rw-r--r-- | tests/topotests/bfd_static_vrf/r1/show_ip_route_step1.json | 31 | ||||
| -rw-r--r-- | tests/topotests/bfd_static_vrf/r1/show_ipv6_route.json | 55 | ||||
| -rw-r--r-- | tests/topotests/bfd_static_vrf/r1/show_ipv6_route_step1.json | 31 | ||||
| -rw-r--r-- | tests/topotests/bfd_static_vrf/r2/frr.conf | 10 | ||||
| -rw-r--r-- | tests/topotests/bfd_static_vrf/r3/frr.conf | 10 | ||||
| -rwxr-xr-x | tests/topotests/bfd_static_vrf/test_bfd_static_vrf.py | 185 | 
11 files changed, 580 insertions, 0 deletions
diff --git a/tests/topotests/bfd_static_vrf/__init__.py b/tests/topotests/bfd_static_vrf/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bfd_static_vrf/__init__.py diff --git a/tests/topotests/bfd_static_vrf/r1/frr.conf b/tests/topotests/bfd_static_vrf/r1/frr.conf new file mode 100644 index 0000000000..fed2c1a2f7 --- /dev/null +++ b/tests/topotests/bfd_static_vrf/r1/frr.conf @@ -0,0 +1,23 @@ +interface r1-eth0 vrf cust1 + ip address 192.168.0.1/24 + ipv6 address fc00:1::0:1/96 +! +interface r1-eth1 vrf cust1 + ip address 192.168.5.1/24 + ipv6 address fc00:2::0:1/96 +! +ip route 0.0.0.0/0 192.168.5.4 bfd vrf cust1 +ipv6 route 0::0/0 fc00:2::4 bfd vrf cust1 +ip route 0.0.0.0/0 192.168.0.2 10 bfd vrf cust1 +ipv6 route 0::0/0 fc00:1::2 10 bfd vrf cust1 +! +bfd + peer 192.168.0.2 vrf cust1 + ! + peer 192.168.5.4 vrf cust1 + ! + peer fc00:1::2 vrf cust1 + ! + peer fc00:2::4 vrf cust1 + ! +! diff --git a/tests/topotests/bfd_static_vrf/r1/show_bfd_peers.json b/tests/topotests/bfd_static_vrf/r1/show_bfd_peers.json new file mode 100644 index 0000000000..77b9ac9114 --- /dev/null +++ b/tests/topotests/bfd_static_vrf/r1/show_bfd_peers.json @@ -0,0 +1,90 @@ +[ +  { +    "multihop": false, +    "peer": "192.168.5.4", +    "vrf": "cust1", +    "passive-mode": false, +    "status": "up", +    "diagnostic": "ok", +    "remote-diagnostic": "ok", +    "type": "configured", +    "receive-interval": 300, +    "transmit-interval": 300, +    "echo-receive-interval": 50, +    "echo-transmit-interval": 0, +    "detect-multiplier": 3, +    "remote-receive-interval": 300, +    "remote-transmit-interval": 300, +    "remote-echo-receive-interval": 50, +    "remote-detect-multiplier": 3, +    "rtt-min": 0, +    "rtt-avg": 0, +    "rtt-max": 0 +  }, +  { +    "multihop": false, +    "peer": "fc00:2::4", +    "vrf": "cust1", +    "passive-mode": false, +    "status": "up", +    "diagnostic": "ok", +    "remote-diagnostic": "ok", +    "type": "configured", +    "receive-interval": 300, +    "transmit-interval": 300, +    "echo-receive-interval": 50, +    "echo-transmit-interval": 0, +    "detect-multiplier": 3, +    "remote-receive-interval": 300, +    "remote-transmit-interval": 300, +    "remote-echo-receive-interval": 50, +    "remote-detect-multiplier": 3, +    "rtt-min": 0, +    "rtt-avg": 0, +    "rtt-max": 0 +  }, +  { +    "multihop": false, +    "peer": "192.168.0.2", +    "vrf": "cust1", +    "passive-mode": false, +    "status": "up", +    "diagnostic": "ok", +    "remote-diagnostic": "ok", +    "type": "configured", +    "receive-interval": 300, +    "transmit-interval": 300, +    "echo-receive-interval": 50, +    "echo-transmit-interval": 0, +    "detect-multiplier": 3, +    "remote-receive-interval": 300, +    "remote-transmit-interval": 300, +    "remote-echo-receive-interval": 50, +    "remote-detect-multiplier": 3, +    "rtt-min": 0, +    "rtt-avg": 0, +    "rtt-max": 0 +  }, +  { +    "multihop": false, +    "peer": "fc00:1::2", +    "vrf": "cust1", +    "passive-mode": false, +    "status": "up", +    "diagnostic": "ok", +    "remote-diagnostic": "ok", +    "type": "configured", +    "receive-interval": 300, +    "transmit-interval": 300, +    "echo-receive-interval": 50, +    "echo-transmit-interval": 0, +    "detect-multiplier": 3, +    "remote-receive-interval": 300, +    "remote-transmit-interval": 300, +    "remote-echo-receive-interval": 50, +    "remote-detect-multiplier": 3, +    "rtt-min": 0, +    "rtt-avg": 0, +    "rtt-max": 0 +  } +] diff --git a/tests/topotests/bfd_static_vrf/r1/show_bfd_peers_step1.json b/tests/topotests/bfd_static_vrf/r1/show_bfd_peers_step1.json new file mode 100644 index 0000000000..0db00a89c5 --- /dev/null +++ b/tests/topotests/bfd_static_vrf/r1/show_bfd_peers_step1.json @@ -0,0 +1,90 @@ +[ +  { +    "multihop": false, +    "peer": "fc00:2::4", +    "vrf": "cust1", +    "passive-mode": false, +    "status": "down", +    "diagnostic": "control detection time expired", +    "remote-diagnostic": "ok", +    "type": "configured", +    "receive-interval": 300, +    "transmit-interval": 300, +    "echo-receive-interval": 50, +    "echo-transmit-interval": 0, +    "detect-multiplier": 3, +    "remote-receive-interval": 300, +    "remote-transmit-interval": 300, +    "remote-echo-receive-interval": 50, +    "remote-detect-multiplier": 3, +    "rtt-min": 0, +    "rtt-avg": 0, +    "rtt-max": 0 +  }, +  { +    "multihop": false, +    "peer": "192.168.0.2", +    "vrf": "cust1", +    "passive-mode": false, +    "status": "up", +    "diagnostic": "ok", +    "remote-diagnostic": "ok", +    "type": "configured", +    "receive-interval": 300, +    "transmit-interval": 300, +    "echo-receive-interval": 50, +    "echo-transmit-interval": 0, +    "detect-multiplier": 3, +    "remote-receive-interval": 300, +    "remote-transmit-interval": 300, +    "remote-echo-receive-interval": 50, +    "remote-detect-multiplier": 3, +    "rtt-min": 0, +    "rtt-avg": 0, +    "rtt-max": 0 +  }, +  { +    "multihop": false, +    "peer": "192.168.5.4", +    "vrf": "cust1", +    "passive-mode": false, +    "status": "down", +    "diagnostic": "control detection time expired", +    "remote-diagnostic": "ok", +    "type": "configured", +    "receive-interval": 300, +    "transmit-interval": 300, +    "echo-receive-interval": 50, +    "echo-transmit-interval": 0, +    "detect-multiplier": 3, +    "remote-receive-interval": 300, +    "remote-transmit-interval": 300, +    "remote-echo-receive-interval": 50, +    "remote-detect-multiplier": 3, +    "rtt-min": 0, +    "rtt-avg": 0, +    "rtt-max": 0 +  }, +  { +    "multihop": false, +    "peer": "fc00:1::2", +    "vrf": "cust1", +    "passive-mode": false, +    "status": "up", +    "diagnostic": "ok", +    "remote-diagnostic": "ok", +    "type": "configured", +    "receive-interval": 300, +    "transmit-interval": 300, +    "echo-receive-interval": 50, +    "echo-transmit-interval": 0, +    "detect-multiplier": 3, +    "remote-receive-interval": 300, +    "remote-transmit-interval": 300, +    "remote-echo-receive-interval": 50, +    "remote-detect-multiplier": 3, +    "rtt-min": 0, +    "rtt-avg": 0, +    "rtt-max": 0 +  } +] diff --git a/tests/topotests/bfd_static_vrf/r1/show_ip_route.json b/tests/topotests/bfd_static_vrf/r1/show_ip_route.json new file mode 100644 index 0000000000..6d537b95ba --- /dev/null +++ b/tests/topotests/bfd_static_vrf/r1/show_ip_route.json @@ -0,0 +1,55 @@ +{ +  "0.0.0.0/0": [ +    { +      "prefix": "0.0.0.0/0", +      "prefixLen": 0, +      "protocol": "static", +      "vrfName": "cust1", +      "selected": true, +      "destSelected": true, +      "distance": 1, +      "metric": 0, +      "installed": true, +      "table": 10, +      "internalStatus": 16, +      "internalFlags": 73, +      "internalNextHopNum": 1, +      "internalNextHopActiveNum": 1, +      "nexthops": [ +        { +          "flags": 3, +          "fib": true, +          "ip": "192.168.5.4", +          "afi": "ipv4", +          "interfaceName": "r1-eth1", +          "active": true, +          "weight": 1 +        } +      ] +    }, +    { +      "prefix": "0.0.0.0/0", +      "prefixLen": 0, +      "protocol": "static", +      "vrfName": "cust1", +      "selected": null, +      "destSelected": null, +      "distance": 10, +      "metric": 0, +      "table": 10, +      "internalStatus": 0, +      "internalFlags": 65, +      "internalNextHopNum": 1, +      "internalNextHopActiveNum": 1, +      "nexthops": [ +        { +          "ip": "192.168.0.2", +          "afi": "ipv4", +          "interfaceName": "r1-eth0", +          "active": true, +          "weight": 1 +        } +      ] +    } +  ] +} diff --git a/tests/topotests/bfd_static_vrf/r1/show_ip_route_step1.json b/tests/topotests/bfd_static_vrf/r1/show_ip_route_step1.json new file mode 100644 index 0000000000..858c308359 --- /dev/null +++ b/tests/topotests/bfd_static_vrf/r1/show_ip_route_step1.json @@ -0,0 +1,31 @@ +{ +  "0.0.0.0/0": [ +    { +      "prefix": "0.0.0.0/0", +      "prefixLen": 0, +      "protocol": "static", +      "vrfName": "cust1", +      "selected": true, +      "destSelected": true, +      "distance": 10, +      "metric": 0, +      "installed": true, +      "table": 10, +      "internalStatus": 16, +      "internalFlags": 73, +      "internalNextHopNum": 1, +      "internalNextHopActiveNum": 1, +      "nexthops": [ +        { +          "flags": 3, +          "fib": true, +          "ip": "192.168.0.2", +          "afi": "ipv4", +          "interfaceName": "r1-eth0", +          "active": true, +          "weight": 1 +        } +      ] +    } +  ] +} diff --git a/tests/topotests/bfd_static_vrf/r1/show_ipv6_route.json b/tests/topotests/bfd_static_vrf/r1/show_ipv6_route.json new file mode 100644 index 0000000000..b002764c9f --- /dev/null +++ b/tests/topotests/bfd_static_vrf/r1/show_ipv6_route.json @@ -0,0 +1,55 @@ +{ +  "::/0": [ +    { +      "prefix": "::/0", +      "prefixLen": 0, +      "protocol": "static", +      "vrfName": "cust1", +      "selected": true, +      "destSelected": true, +      "distance": 1, +      "metric": 0, +      "installed": true, +      "table": 10, +      "internalStatus": 16, +      "internalFlags": 73, +      "internalNextHopNum": 1, +      "internalNextHopActiveNum": 1, +      "nexthops": [ +        { +          "flags": 3, +          "fib": true, +          "ip": "fc00:2::4", +          "afi": "ipv6", +          "interfaceName": "r1-eth1", +          "active": true, +          "weight": 1 +        } +      ] +    }, +    { +      "prefix": "::/0", +      "prefixLen": 0, +      "protocol": "static", +      "vrfName": "cust1", +      "selected": null, +      "destSelected": null, +      "distance": 10, +      "metric": 0, +      "table": 10, +      "internalStatus": 0, +      "internalFlags": 65, +      "internalNextHopNum": 1, +      "internalNextHopActiveNum": 1, +      "nexthops": [ +        { +          "ip": "fc00:1::2", +          "afi": "ipv6", +          "interfaceName": "r1-eth0", +          "active": true, +          "weight": 1 +        } +      ] +    } +  ] +} diff --git a/tests/topotests/bfd_static_vrf/r1/show_ipv6_route_step1.json b/tests/topotests/bfd_static_vrf/r1/show_ipv6_route_step1.json new file mode 100644 index 0000000000..f9b931efd8 --- /dev/null +++ b/tests/topotests/bfd_static_vrf/r1/show_ipv6_route_step1.json @@ -0,0 +1,31 @@ +{ +  "::/0": [ +    { +      "prefix": "::/0", +      "prefixLen": 0, +      "protocol": "static", +      "vrfName": "cust1", +      "selected": true, +      "destSelected": true, +      "distance": 10, +      "metric": 0, +      "installed": true, +      "table": 10, +      "internalStatus": 16, +      "internalFlags": 73, +      "internalNextHopNum": 1, +      "internalNextHopActiveNum": 1, +      "nexthops": [ +        { +          "flags": 3, +          "fib": true, +          "ip": "fc00:1::2", +          "afi": "ipv6", +          "interfaceName": "r1-eth0", +          "active": true, +          "weight": 1 +        } +      ] +    } +  ] +} diff --git a/tests/topotests/bfd_static_vrf/r2/frr.conf b/tests/topotests/bfd_static_vrf/r2/frr.conf new file mode 100644 index 0000000000..984e3e1e46 --- /dev/null +++ b/tests/topotests/bfd_static_vrf/r2/frr.conf @@ -0,0 +1,10 @@ +interface r2-eth0 vrf cust1 + ip address 192.168.0.2/24 + ipv6 address fc00:1::0:2/96 +! +bfd + peer 192.168.0.1 vrf cust1 + ! + peer fc00:1::1 vrf cust1 + ! +! diff --git a/tests/topotests/bfd_static_vrf/r3/frr.conf b/tests/topotests/bfd_static_vrf/r3/frr.conf new file mode 100644 index 0000000000..0b8140983e --- /dev/null +++ b/tests/topotests/bfd_static_vrf/r3/frr.conf @@ -0,0 +1,10 @@ +interface r3-eth0 vrf cust1 + ip address 192.168.5.4/24 + ipv6 address fc00:2::0:4/96 +! +bfd + peer 192.168.5.1 vrf cust1 + ! + peer fc00:2::1 vrf cust1 + ! +! diff --git a/tests/topotests/bfd_static_vrf/test_bfd_static_vrf.py b/tests/topotests/bfd_static_vrf/test_bfd_static_vrf.py new file mode 100755 index 0000000000..deb618c399 --- /dev/null +++ b/tests/topotests/bfd_static_vrf/test_bfd_static_vrf.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bfd_static_vrf.py +# Part of NetDEF Topology Tests +# +# Copyright 2025 6WIND S.A. +# + +""" +test_bfd_static_vrf.py: Test the FRR static routes with BFD tracking. +""" + +import os +import sys +import json +import platform +import functools +import pytest + +pytestmark = [pytest.mark.staticd, pytest.mark.bfdd] + +# 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 + + +def build_topo(tgen): +    "Build function" + +    # Create 3 routers +    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 = tgen.add_switch("s2") +    switch.add_link(tgen.gears["r1"]) +    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(): +        tgen.net[rname].cmd( +            f""" +ip link add cust1 type vrf table 10 +ip link set dev cust1 up +ip link set dev {rname}-eth0 master cust1 +sysctl net.ipv6.conf.{rname}-eth0.keep_addr_on_down=1 +""" +        ) + +    tgen.net["r1"].cmd( +        """ +ip link set dev r1-eth1 master cust1 +sysctl net.ipv6.conf.r1-eth1.keep_addr_on_down=1 +""" +    ) + +    for _, (rname, router) in enumerate(router_list.items(), 1): +        router.load_frr_config( +            os.path.join(CWD, "{}/frr.conf".format(rname)), +            [ +                (TopoRouter.RD_ZEBRA, None), +                (TopoRouter.RD_MGMTD, None), +                (TopoRouter.RD_BFD, None), +                (TopoRouter.RD_STATIC, None), +            ], +        ) + +    # Initialize all routers. +    tgen.start_router() + + +def teardown_module(_mod): +    "Teardown the pytest environment" + +    tgen = get_topogen() +    tgen.stop_topology() + + +def check_bfd_state(step=None): +    tgen = get_topogen() + +    r1 = tgen.gears["r1"] + +    step_suffix = f"_step{step}" if step else "" + +    logger.info("Check BFD entries") +    reffile = os.path.join(CWD, f"r1/show_bfd_peers{step_suffix}.json") +    expected = json.loads(open(reffile).read()) +    cmd = "show bfd peers json" +    test_func = functools.partial(topotest.router_json_cmp, r1, cmd, expected) +    _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) +    assertmsg = f"BFD did not converge. Error on r1 {cmd}" +    assert res is None, assertmsg + +    logger.info("Check IPv4 default route") +    reffile = os.path.join(CWD, f"r1/show_ip_route{step_suffix}.json") +    expected = json.loads(open(reffile).read()) +    cmd = "show ip route vrf cust1 0.0.0.0/0 json" +    test_func = functools.partial(topotest.router_json_cmp, r1, cmd, expected) +    _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) +    assertmsg = f"BFD did not converge. Error on r1 {cmd}" +    assert res is None, assertmsg + +    logger.info("Check IPv6 default route") +    reffile = os.path.join(CWD, f"r1/show_ipv6_route{step_suffix}.json") +    expected = json.loads(open(reffile).read()) +    cmd = "show ipv6 route vrf cust1 ::/0 json" +    test_func = functools.partial(topotest.router_json_cmp, r1, cmd, expected) +    _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) +    assertmsg = f"BFD did not converge. Error on r1 {cmd}" +    assert res is None, assertmsg + + +def test_bfd_convergence(): +    "Assert that the BFD peers can find themselves." + +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    check_bfd_state() + + +def test_bfd_static_vrf_step1(): +    """ +    Assert that BFD notices the link down after simulating network +    failure. +    """ + +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    logger.info("Set r3-eth0 down") +    tgen.gears["r3"].link_enable("r3-eth0", enabled=False) + +    check_bfd_state(step=1) + + +def test_bfd_static_vrf_step2(): +    """ +    Assert that BFD goes back to the nominal stater after links are back up. +    """ + +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    logger.info("Set r3-eth0 up") +    tgen.gears["r3"].link_enable("r3-eth0", enabled=True) + +    check_bfd_state() + + +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))  | 
