diff --git a/tests/topotests/static_vrf/r1/frr.conf b/tests/topotests/static_vrf/r1/frr.conf new file mode 100644 index 000000000000..8d3d20734a1d --- /dev/null +++ b/tests/topotests/static_vrf/r1/frr.conf @@ -0,0 +1,20 @@ +debug static route + +interface r1-eth0 vrf red + ip address 1.1.1.1/24 +exit + +interface r1-eth1 vrf blue + ip address 2.2.2.1/24 +exit + +ip route 30.30.30.30/32 1.1.1.2 nexthop-vrf red +ip route 30.30.30.30/32 2.2.2.2 nexthop-vrf blue +ip route 31.31.31.31/32 r1-eth0 nexthop-vrf red +ip route 31.31.31.31/32 r1-eth1 nexthop-vrf blue + +ip route 10.10.10.10/32 2.2.2.2 vrf red nexthop-vrf blue +ip route 11.11.11.11/32 r1-eth1 vrf red nexthop-vrf blue + +ip route 20.20.20.20/32 1.1.1.2 vrf blue nexthop-vrf red +ip route 21.21.21.21/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 000000000000..3fa673fb68e6 --- /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, "30.30.30.30", ["1.1.1.2", "2.2.2.2"], None) + check_kernel(r1, "31.31.31.31", ["r1-eth0", "r1-eth1"], None) + check_kernel(r1, "10.10.10.10", ["2.2.2.2"], "red") + check_kernel(r1, "11.11.11.11", ["r1-eth1"], "red") + check_kernel(r1, "20.20.20.20", ["1.1.1.2"], "blue") + check_kernel(r1, "21.21.21.21", ["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, "30.30.30.30", ["1.1.1.2"], None, expected_nh=False) + check_kernel(r1, "30.30.30.30", ["2.2.2.2"], None) + check_kernel(r1, "31.31.31.31", ["r1-eth0"], None, expected_nh=False) + check_kernel(r1, "31.31.31.31", ["r1-eth1"], None) + check_kernel(r1, "20.20.20.20", ["1.1.1.2"], "blue", expected_p=False) + check_kernel(r1, "21.21.21.21", ["r1-eth0"], "blue", expected_p=False) + + # Delete VRF blue + r1.net.del_iface("blue") + + # Check that "blue" nexthops are removed + check_kernel(r1, "30.30.30.30", ["2.2.2.2"], None, expected_p=False) + check_kernel(r1, "31.31.31.31", ["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, "30.30.30.30", ["1.1.1.2"], None) + check_kernel(r1, "31.31.31.31", ["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, "30.30.30.30", ["1.1.1.2", "2.2.2.2"], None) + check_kernel(r1, "31.31.31.31", ["r1-eth0", "r1-eth1"], None) + check_kernel(r1, "10.10.10.10", ["2.2.2.2"], "red") + check_kernel(r1, "11.11.11.11", ["r1-eth1"], "red") + check_kernel(r1, "20.20.20.20", ["1.1.1.2"], "blue") + check_kernel(r1, "21.21.21.21", ["r1-eth0"], "blue")