]> git.puffer.fish Git - mirror/frr.git/commitdiff
tests: add topotest for igmp proxy 16861/head
authorBarry A. Trent <barry.trent@atcorp.com>
Tue, 17 Sep 2024 20:15:27 +0000 (13:15 -0700)
committerBarry A. Trent <barry.trent@atcorp.com>
Mon, 23 Sep 2024 18:43:40 +0000 (11:43 -0700)
Signed-off-by: Barry A. Trent <barry.trent@atcorp.com>
tests/topotests/lib/pim.py
tests/topotests/pim_basic_igmp_proxy/r1/frr.conf [new file with mode: 0644]
tests/topotests/pim_basic_igmp_proxy/r2/frr.conf [new file with mode: 0644]
tests/topotests/pim_basic_igmp_proxy/r3/frr.conf [new file with mode: 0644]
tests/topotests/pim_basic_igmp_proxy/rp/frr.conf [new file with mode: 0644]
tests/topotests/pim_basic_igmp_proxy/test_pim_igmp_proxy.py [new file with mode: 0644]

index eb3723be42f893193b640bcc99c4a5c5c7680983..2062f655611c8d041d37c01dd710306351203792 100644 (file)
@@ -4260,6 +4260,7 @@ def verify_local_igmp_groups(tgen, dut, interface, group_addresses):
     logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
     return True
 
+
 @retry(retry_timeout=62)
 def verify_static_groups(tgen, dut, interface, group_addresses):
     """
@@ -4293,7 +4294,9 @@ def verify_static_groups(tgen, dut, interface, group_addresses):
     rnode = tgen.routers()[dut]
 
     logger.info("[DUT: %s]: Verifying static groups received:", dut)
-    show_static_group_json = run_frr_cmd(rnode, "show ip igmp static-group json", isjson=True)
+    show_static_group_json = run_frr_cmd(
+        rnode, "show ip igmp static-group json", isjson=True
+    )
 
     if type(group_addresses) is not list:
         group_addresses = [group_addresses]
@@ -4330,6 +4333,71 @@ def verify_static_groups(tgen, dut, interface, group_addresses):
     logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
     return True
 
+
+@retry(retry_timeout=62)
+def verify_local_igmp_proxy_groups(
+    tgen, dut, group_addresses_present, group_addresses_not_present
+):
+    """
+    Verify igmp proxy groups are as expected by running
+    "show ip igmp static-group json" command
+
+    Parameters
+    ----------
+    * `tgen`: topogen object
+    * `dut`: device under test
+    * `group_addresses_present`: IGMP group addresses which should
+                                 currently be proxied
+    * `group_addresses_not_present`: IGMP group addresses which should
+                                     not currently be proxied
+
+    Usage
+    -----
+    dut = "r1"
+    group_addresses_present = "225.1.1.1"
+    group_addresses_not_present = "225.2.2.2"
+    result = verify_igmp_proxy_groups(tgen, dut, group_p, group_np)
+
+    Returns
+    -------
+    errormsg(str) or True
+    """
+
+    if dut not in tgen.routers():
+        errormsg = "[DUT %s]: Device not found!"
+        return errormsg
+
+    rnode = tgen.routers()[dut]
+
+    logger.info("[DUT: %s]: Verifying local IGMP proxy groups:", dut)
+
+    out = rnode.vtysh_cmd("show ip igmp proxy json", isjson=True)
+    groups = [g["group"] if "group" in g else None for g in out["r1-eth1"]["groups"]]
+
+    if type(group_addresses_present) is not list:
+        group_addresses_present = [group_addresses_present]
+    if type(group_addresses_not_present) is not list:
+        group_addresses_not_present = [group_addresses_not_present]
+
+    for test_addr in group_addresses_present:
+        if not test_addr in groups:
+            errormsg = (
+                "[DUT %s]: Verifying local IGMP proxy joins FAILED!! "
+                " Expected but not found: %s " % (dut, test_addr)
+            )
+            return errormsg
+
+    for test_addr in group_addresses_not_present:
+        if test_addr in groups:
+            errormsg = (
+                "[DUT %s]: Verifying local IGMP proxy join removed FAILED!! "
+                " Unexpected but found: %s " % (dut, test_addr)
+            )
+            return errormsg
+
+    return True
+
+
 def verify_pim_interface_traffic(tgen, input_dict, return_stats=True, addr_type="ipv4"):
     """
     Verify ip pim interface traffic by running
diff --git a/tests/topotests/pim_basic_igmp_proxy/r1/frr.conf b/tests/topotests/pim_basic_igmp_proxy/r1/frr.conf
new file mode 100644 (file)
index 0000000..72d031e
--- /dev/null
@@ -0,0 +1,29 @@
+hostname r1
+!
+interface r1-eth0
+  ip address 10.0.20.1/24
+  ip igmp
+  ip pim
+  ip igmp join 225.1.1.1
+  ip igmp join 225.2.2.2
+!
+interface r1-eth1
+  ip address 10.0.30.1/24
+  ip pim
+  ip igmp
+  ip igmp proxy
+!
+interface r1-eth2
+  ip address 10.0.40.1/24
+  ip igmp
+  ip pim
+  ip igmp join 225.3.3.3
+  ip igmp join 225.4.4.4
+!
+interface lo
+  ip address 10.254.0.1/32
+  ip pim
+!
+router pim
+  rp 10.254.0.3
+  join-prune-interval 5
diff --git a/tests/topotests/pim_basic_igmp_proxy/r2/frr.conf b/tests/topotests/pim_basic_igmp_proxy/r2/frr.conf
new file mode 100644 (file)
index 0000000..08f721d
--- /dev/null
@@ -0,0 +1,19 @@
+hostname r2
+!
+interface r2-eth0
+ ip address 10.0.20.2/24
+  ip igmp
+  ip pim
+  ip igmp proxy
+!
+interface r2-eth1
+ ip address 10.0.80.1/24
+ ip igmp
+ ip pim passive
+!
+interface lo
+ ip address 10.254.0.2/32
+!
+router pim
+  rp 10.254.0.3
+  join-prune-interval 5
diff --git a/tests/topotests/pim_basic_igmp_proxy/r3/frr.conf b/tests/topotests/pim_basic_igmp_proxy/r3/frr.conf
new file mode 100644 (file)
index 0000000..8e58e8c
--- /dev/null
@@ -0,0 +1,8 @@
+hostname r3
+!
+interface r3-eth0
+ ip address 10.0.40.4/24
+!
+interface lo
+ ip address 10.254.0.4/32
+!
diff --git a/tests/topotests/pim_basic_igmp_proxy/rp/frr.conf b/tests/topotests/pim_basic_igmp_proxy/rp/frr.conf
new file mode 100644 (file)
index 0000000..ed60fdd
--- /dev/null
@@ -0,0 +1,16 @@
+hostname rp
+!
+interface rp-eth0
+ ip address 10.0.30.3/24
+ ip pim
+!
+interface lo
+  ip address 10.254.0.3/32
+  ip pim
+!
+router pim
+  join-prune-interval 5
+  rp 10.254.0.3
+  register-accept-list ACCEPT
+
+ip prefix-list ACCEPT seq 5 permit 10.0.20.0/24 le 32
diff --git a/tests/topotests/pim_basic_igmp_proxy/test_pim_igmp_proxy.py b/tests/topotests/pim_basic_igmp_proxy/test_pim_igmp_proxy.py
new file mode 100644 (file)
index 0000000..b6804fa
--- /dev/null
@@ -0,0 +1,319 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_pim_igmp_proxy.py
+#
+# Copyright (c) 2024 ATCorp
+# Barry A. Trent
+#
+
+"""
+Following tests are covered to test pim igmp proxy:
+
+1. TC:1 Verify correct joins were read from the config and proxied
+2. TC:2 Verify joins from another interface are proxied
+3. TC:3 Verify correct proxy disable on 'no ip igmp proxy'
+4. TC:4 Verify that proper proxy joins are set up on run-time enable
+5. TC:5 Verify igmp drops/timeouts from another interface cause
+        proxy join removal
+"""
+
+import os
+import sys
+import pytest
+import json
+import time
+from functools import partial
+
+pytestmark = [pytest.mark.pimd]
+
+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
+from lib.pim import verify_local_igmp_proxy_groups
+
+
+def build_topo(tgen):
+    "Build function"
+
+    for routern in range(1, 4):
+        tgen.add_router("r{}".format(routern))
+
+    tgen.add_router("rp")
+
+    #   rp ------ r1 -------- r2 -------
+    #              \
+    #               --------- r3
+    # r1 -> .1
+    # r2 -> .2
+    # rp -> .3
+    # r3 -> .4
+    # loopback network is 10.254.0.X/32
+    #
+    # r1 <- sw1 -> r2
+    # r1-eth0 <-> r2-eth0
+    # 10.0.20.0/24
+    sw = tgen.add_switch("sw1")
+    sw.add_link(tgen.gears["r1"])
+    sw.add_link(tgen.gears["r2"])
+
+    # r1 <- sw2 -> rp
+    # r1-eth1 <-> rp-eth0
+    # 10.0.30.0/24
+    sw = tgen.add_switch("sw2")
+    sw.add_link(tgen.gears["r1"])
+    sw.add_link(tgen.gears["rp"])
+
+    # 10.0.40.0/24
+    sw = tgen.add_switch("sw3")
+    sw.add_link(tgen.gears["r1"])
+    sw.add_link(tgen.gears["r3"])
+
+    # Dummy interface for static joins
+    tgen.gears["r2"].run("ip link add r2-eth1 type dummy")
+
+
+def setup_module(mod):
+    "Sets up the pytest environment"
+    tgen = Topogen(build_topo, mod.__name__)
+    tgen.start_topology()
+
+    # For all registered routers, load the zebra configuration file
+    for rname, router in tgen.routers().items():
+        router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+    # After loading the configurations, this function loads configured daemons.
+    tgen.start_router()
+    # tgen.mininet_cli()
+
+
+def teardown_module():
+    "Teardown the pytest environment"
+    tgen = get_topogen()
+
+    # This function tears down the whole topology.
+    tgen.stop_topology()
+
+
+def test_pim_igmp_proxy_config():
+    "Ensure correct joins were read from the config and proxied"
+    logger.info("Verify initial igmp proxy setup from config file")
+    tgen = get_topogen()
+
+    r1 = tgen.gears["r1"]
+
+    expected = {
+        "vrf": "default",
+        "r1-eth1": {
+            "name": "r1-eth1",
+            "groups": [
+                {
+                    "source": "*",
+                    "group": "225.4.4.4",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.3.3.3",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.2.2.2",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.1.1.1",
+                    "primaryAddr": "10.0.30.1",
+                },
+            ],
+        },
+    }
+
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip igmp proxy json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+    assertmsg = '"{}" JSON output mismatches'.format(r1.name)
+    assert result is None, assertmsg
+    # tgen.mininet_cli()
+
+
+def test_pim_igmp_proxy_learn():
+    "Ensure joins learned from a neighbor are propagated"
+    logger.info("Verify joins can be learned")
+    tgen = get_topogen()
+
+    r1 = tgen.gears["r1"]
+    r2 = tgen.gears["r2"]
+
+    r2.vtysh_cmd(
+        "conf\nint r2-eth0\nip igmp join 225.5.5.5\nip igmp join 225.6.6.6\nexit\nexit"
+    )
+    r2.vtysh_cmd(
+        "conf\nint r2-eth1\nip igmp join 225.7.7.7\nip igmp join 225.8.8.8\nexit\nexit"
+    )
+    expected = {
+        "vrf": "default",
+        "r1-eth1": {
+            "name": "r1-eth1",
+            "groups": [
+                {
+                    "source": "*",
+                    "group": "225.5.5.5",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.6.6.6",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.7.7.7",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.8.8.8",
+                    "primaryAddr": "10.0.30.1",
+                },
+            ],
+        },
+    }
+
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip igmp proxy json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+    assertmsg = '"{}" JSON output mismatches'.format(r1.name)
+    assert result is None, assertmsg
+    # tgen.mininet_cli()
+
+
+def test_pim_no_igmp_proxy():
+    "Check for correct proxy disable"
+    logger.info("Verify no ip igmp proxy")
+    tgen = get_topogen()
+
+    r1 = tgen.gears["r1"]
+
+    r1.vtysh_cmd("conf\nint r1-eth1\nno ip igmp proxy\nexit\nexit")
+    expected = {"vrf": "default"}
+
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip igmp proxy json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+    assertmsg = '"{}" JSON output mismatches'.format(r1.name)
+    assert result is None, assertmsg
+    # tgen.mininet_cli()
+
+
+def test_pim_igmp_proxy_restart():
+    "Check that all proxy joins are captured at run-time enable"
+    logger.info("Verify runtime ip igmp proxy")
+    tgen = get_topogen()
+
+    r1 = tgen.gears["r1"]
+
+    r1.vtysh_cmd("conf\nint r1-eth1\nip igmp proxy\nexit\nexit")
+    expected = {
+        "vrf": "default",
+        "r1-eth1": {
+            "name": "r1-eth1",
+            "groups": [
+                {
+                    "source": "*",
+                    "group": "225.8.8.8",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.7.7.7",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.6.6.6",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.5.5.5",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.4.4.4",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.3.3.3",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.2.2.2",
+                    "primaryAddr": "10.0.30.1",
+                },
+                {
+                    "source": "*",
+                    "group": "225.1.1.1",
+                    "primaryAddr": "10.0.30.1",
+                },
+            ],
+        },
+    }
+
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip igmp proxy json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+    assertmsg = '"{}" JSON output mismatches'.format(r1.name)
+    assert result is None, assertmsg
+    # tgen.mininet_cli()
+
+
+def test_pim_igmp_proxy_leave():
+    "Ensure drops/timeouts learned from a neighbor are propagated"
+    logger.info("Verify joins can be dropped")
+    tgen = get_topogen()
+
+    r1 = tgen.gears["r1"]
+    r2 = tgen.gears["r2"]
+
+    r1.vtysh_cmd("conf\nint r1-eth0\nno ip igmp join 225.1.1.1\nexit\nexit")
+    r2.vtysh_cmd("conf\nint r2-eth0\nno ip igmp join 225.6.6.6\nexit\nexit")
+    r2.vtysh_cmd("conf\nint r2-eth1\nno ip igmp join 225.8.8.8\nexit\nexit")
+
+    joined_addresses = ["225.2.2.2", "225.3.3.3", "225.4.4.4", "225.5.5.5", "225.7.7.7"]
+    deleted_addresses = ["225.1.1.1", "225.6.6.6", "225.8.8.8"]
+
+    result = verify_local_igmp_proxy_groups(
+        tgen, "r1", joined_addresses, deleted_addresses
+    )
+
+    assert result is True, "Error: {}".format(result)
+    # 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))