diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
commit | 2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch) | |
tree | c05dc0f8e6aa3accc84e3e5cffc933ed94941383 /tests/topotests/multicast_pim_dr_nondr_test | |
parent | Initial commit. (diff) | |
download | frr-upstream.tar.xz frr-upstream.zip |
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/topotests/multicast_pim_dr_nondr_test')
7 files changed, 3429 insertions, 0 deletions
diff --git a/tests/topotests/multicast_pim_dr_nondr_test/__init__.py b/tests/topotests/multicast_pim_dr_nondr_test/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/topotests/multicast_pim_dr_nondr_test/__init__.py diff --git a/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_ospf_topo2.json b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_ospf_topo2.json new file mode 100644 index 0000000..4a4eb9a --- /dev/null +++ b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_ospf_topo2.json @@ -0,0 +1,195 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "switches": { + "s1": { + "links": { + "i1": {"ipv4": "auto"}, + "r1": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"} + } + } + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r4": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "neighbors": { + "r4": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r4": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "neighbors": { + "r4": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r5": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "neighbors": { + "r1": {}, + "r2": {}, + "r5": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r4": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "i2": { + "ipv4": "auto", + "pim": "enable" + } + }, + "ospf": { + "neighbors": { + "r4": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "i1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback"} + } + }, + "i2": { + "links": { + "r5": {"ipv4": "auto"} + } + } + } +} diff --git a/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_static_routes_topo1.json b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_static_routes_topo1.json new file mode 100644 index 0000000..9d7d766 --- /dev/null +++ b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_static_routes_topo1.json @@ -0,0 +1,85 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "switches": { + "s1": { + "links": { + "i1": {"ipv4": "auto"}, + "r1": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "r3": {"ipv4": "auto", "pim": "enable"} + } + } + }, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r4": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.0.0.1/24", "1.0.5.17/32"], + "next_hop": "10.0.1.2" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r4": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.0.0.1/24", "1.0.5.17/32"], + "next_hop": "10.0.2.2" + }] + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.0.0.1/24", "1.0.5.17/32"], + "next_hop": "10.1.1.1" + }] + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r1": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "r5": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [ + { + "network": ["10.1.1.4/24"], + "next_hop": "10.0.2.1" + }, + { + "network": ["10.0.0.1/24"], + "next_hop": "10.0.3.2" + }] + }, + "r5": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r4": {"ipv4": "auto", "pim": "enable"}, + "i2": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.1.1.4/24", "1.0.5.17/32"], + "next_hop": "10.0.3.1" + }] + }, + "i1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback"} + } + }, + "i2": { + "links": { + "r5": {"ipv4": "auto"} + } + } + } +} diff --git a/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_transit_router_topo3.json b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_transit_router_topo3.json new file mode 100644 index 0000000..f078657 --- /dev/null +++ b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_transit_router_topo3.json @@ -0,0 +1,241 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "switches": { + "s1": { + "links": { + "i1": {"ipv4": "auto"}, + "r1": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"} + } + } + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r6": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "neighbors": { + "r6": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r6": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "neighbors": { + "r6": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r6": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r5": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "neighbors": { + "r6": {}, + "r5": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r4": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "i2": { + "ipv4": "auto", + "pim": "enable" + } + }, + "ospf": { + "neighbors": { + "r4": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r6": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r4": { + "ipv4": "auto", + "pim": "enable", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "neighbors": { + "r1": {}, + "r2": {}, + "r4": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "i1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback"} + } + }, + "i2": { + "links": { + "r5": {"ipv4": "auto"} + } + } + } +} diff --git a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py new file mode 100755 index 0000000..d0422e2 --- /dev/null +++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py @@ -0,0 +1,1139 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test multicast pim sm: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: +1. Configure IGMP local join on DR and non DR +""" + +import os +import sys +import json +import time +import datetime +from time import sleep +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, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + reset_config_on_routers, + shutdown_bringup_interface, + apply_raw_config, + add_interfaces_to_vlan, + kill_router_daemons, + start_router_daemons, + create_static_routes, + check_router_status, + topo_daemons, + required_linux_kernel_version, +) +from lib.pim import ( + create_pim_config, + create_igmp_config, + verify_mroutes, + clear_mroute, + clear_pim_interface_traffic, + verify_pim_config, + verify_upstream_iif, + verify_multicast_traffic, + verify_multicast_flag_state, + verify_igmp_groups, + McastTesterHelper, +) +from lib.topolog import logger +from lib.topojson import build_config_from_json + +HELLO_TIMER = 1 +HOLD_TIMER = 3 + +pytestmark = [pytest.mark.pimd] + +TOPOLOGY = """ + +Descripton: Configuring OSPF on r1/r2/r4/r5 for RP reachablility. +IPs are assigned automatically to routers, start IP and subnet is defined in respective JSON file +JSON snippet: + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24}, + + r5 ------- i2 + 10.0.3.2/24| 10.0.0.2/24 + | + 10.0.3.1/24| + ------------ r4 ---------- + | 10.0.1.2/24 10.0.2.2/24 | + 10.0.1.1/24 | | 10.0.2.1/24 + r1 ----------- s1 ---------- r2 + 10.0.4.2/24 | 10.0.4.3/24 + | + |10.0.4.1/24 + i1 + + Description: + i1, i2 - FRR running iperf to send IGMP + join and traffic + r1, r2, r4, r5 - FRR router + s1 - OVS switch +""" + +# Global variables +VLAN_1 = 2501 +GROUP_RANGE = "225.0.0.0/8" +IGMP_JOIN = "225.1.1.1" +VLAN_INTF_ADRESS_1 = "10.0.8.3/24" +SAME_VLAN_IP_1 = {"ip": "10.1.1.1", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_2 = {"ip": "10.1.1.2", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_3 = {"ip": "10.1.1.3", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_4 = {"ip": "10.1.1.4", "subnet": "255.255.255.0", "cidr": "24"} +GROUP_RANGE_1 = ["225.1.1.1/32", "225.1.1.2/32"] +IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2"] +GROUP_RANGE_2 = ["226.1.1.1/32", "226.1.1.2/32"] +IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2"] +GROUP_RANGE_3 = ["227.1.1.1/32", "227.1.1.2/32"] +IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2"] + +intf_r1_s1 = None +intf_r1_s1_addr = None +intf_r2_s1 = None +intf_r2_s1_addr = None +intf_r3_s1 = None +intf_r3_s1_addr = None +intf_i1_s1 = None +intf_i1_s1_addr = None + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.19") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + testdir = os.path.dirname(os.path.realpath(__file__)) + json_file = "{}/pim_dr_nondr_with_ospf_topo2.json".format(testdir) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, tgen.json_topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, tgen.json_topo) + + # XXX Replace this using "with McastTesterHelper()... " in each test if possible. + global app_helper + app_helper = McastTesterHelper(tgen) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + app_helper.cleanup() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Local APIs +# +##################################################### + + +def pre_config_for_receiver_dr_tests( + tgen, topo, tc_name, highest_priority, lowest_priority +): + """ + API to do common pre-configuration for receiver test cases + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `highest_priority`: router which will be having highest DR priority + * `lowest_priority`: router which will be having lowest DR priority + """ + + global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_i1_s1, intf_i1_s1_addr + + step("Configure IGMP and PIM on switch connected receiver nodes") + step("Configure PIM on all upstream interfaces") + + step("Configure link between R1, R2 ,R3 and receiver on" " same vlan") + step( + "Make sure {0} is DR initially configuring highest IP on {0} and R2 " + "second highest, {1} is lower".format(highest_priority, lowest_priority) + ) + + intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"] + intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"] + + intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"] + intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"] + + intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"] + intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"] + + if lowest_priority == "r1": + lowest_pr_intf = intf_r1_s1 + else: + lowest_pr_intf = intf_r2_s1 + + if highest_priority == "r1": + highest_pr_intf = intf_r1_s1 + else: + highest_pr_intf = intf_r2_s1 + + vlan_input = { + lowest_priority: { + "vlan": { + VLAN_1: [ + { + lowest_pr_intf: { + "ip": SAME_VLAN_IP_1["ip"], + "subnet": SAME_VLAN_IP_1["subnet"], + } + } + ] + } + }, + highest_priority: { + "vlan": { + VLAN_1: [ + { + highest_pr_intf: { + "ip": SAME_VLAN_IP_2["ip"], + "subnet": SAME_VLAN_IP_2["subnet"], + } + } + ] + } + }, + "i1": { + "vlan": { + VLAN_1: [ + { + intf_i1_s1: { + "ip": SAME_VLAN_IP_4["ip"], + "subnet": SAME_VLAN_IP_4["subnet"], + } + } + ] + } + }, + } + + add_interfaces_to_vlan(tgen, vlan_input) + + raw_config = { + "r1": { + "raw_config": [ + "interface {}".format(intf_r1_s1), + "no ip address {}".format(intf_r1_s1_addr), + "no ip pim", + ] + }, + "r2": { + "raw_config": [ + "interface {}".format(intf_r2_s1), + "no ip address {}".format(intf_r2_s1_addr), + "no ip pim", + ] + }, + "i1": { + "raw_config": [ + "interface {}".format(intf_i1_s1), + "no ip address {}".format(intf_i1_s1_addr), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + raw_config = { + lowest_priority: { + "raw_config": [ + "interface {}.{}".format(lowest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]), + "ip pim", + "ip igmp", + "ip igmp version 2", + ] + }, + highest_priority: { + "raw_config": [ + "interface {}.{}".format(highest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]), + "ip pim", + "ip igmp", + "ip igmp version 2", + ] + }, + "i1": { + "raw_config": [ + "interface {}.{}".format(intf_i1_s1, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + raw_config = { + dut: { + "raw_config": [ + "interface {}.{}".format(intf, VLAN_1), + "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24") + + input_dict = { + "r4": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send IGMP join for groups 226.1.1.1 to 226.1.1.5") + + vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1) + result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, join_intf=vlan_intf_i1_s1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Enable OSPF between r1 and r2") + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + raw_config = { + dut: { + "raw_config": [ + "interface {}.{}".format(intf, VLAN_1), + "ip ospf area 0.0.0.0", + "ip ospf dead-interval 4", + "ip ospf hello-interval 1", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Start traffic from R4 connected source") + + result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "r5") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + return True + + +def pre_config_for_source_dr_tests( + tgen, topo, tc_name, highest_priority, lowest_priority +): + """ + API to do common pre-configuration for source test cases + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `highest_priority`: router which will be having highest DR priority + * `lowest_priority`: router which will be having lowest DR priority + """ + + global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_i1_s1, intf_i1_s1_addr + + step("Configure IGMP and PIM on switch connected receiver nodes") + step("Configure PIM on all upstream interfaces") + + step("Configure link between R1, R2 ,R3 and receiver on" " same vlan") + step( + "Make sure {0} is DR initially configuring highest IP on {0} and R2 " + "second highest, {1} is lower".format(highest_priority, lowest_priority) + ) + + intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"] + intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"] + + intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"] + intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"] + + intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"] + intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"] + + if lowest_priority == "r1": + lowest_pr_intf = intf_r1_s1 + else: + lowest_pr_intf = intf_r2_s1 + + if highest_priority == "r1": + highest_pr_intf = intf_r1_s1 + else: + highest_pr_intf = intf_r2_s1 + + vlan_input = { + lowest_priority: { + "vlan": { + VLAN_1: [ + { + lowest_pr_intf: { + "ip": SAME_VLAN_IP_1["ip"], + "subnet": SAME_VLAN_IP_1["subnet"], + } + } + ] + } + }, + highest_priority: { + "vlan": { + VLAN_1: [ + { + highest_pr_intf: { + "ip": SAME_VLAN_IP_2["ip"], + "subnet": SAME_VLAN_IP_2["subnet"], + } + } + ] + } + }, + "i1": { + "vlan": { + VLAN_1: [ + { + intf_i1_s1: { + "ip": SAME_VLAN_IP_4["ip"], + "subnet": SAME_VLAN_IP_4["subnet"], + } + } + ] + } + }, + } + + add_interfaces_to_vlan(tgen, vlan_input) + + raw_config = { + "r1": { + "raw_config": [ + "interface {}".format(intf_r1_s1), + "no ip address {}".format(intf_r1_s1_addr), + "no ip pim", + ] + }, + "r2": { + "raw_config": [ + "interface {}".format(intf_r2_s1), + "no ip address {}".format(intf_r2_s1_addr), + "no ip pim", + ] + }, + "i1": { + "raw_config": [ + "interface {}".format(intf_i1_s1), + "no ip address {}".format(intf_i1_s1_addr), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Configure IGMP and PIM on switch connected receiver nodes , " + "configure PIM nbr with hello timer 1" + ) + + raw_config = { + lowest_priority: { + "raw_config": [ + "interface {}.{}".format(lowest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]), + "ip pim", + ] + }, + highest_priority: { + "raw_config": [ + "interface {}.{}".format(highest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]), + "ip pim", + ] + }, + "i1": { + "raw_config": [ + "interface {}.{}".format(intf_i1_s1, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + raw_config = { + dut: { + "raw_config": [ + "interface {}.{}".format(intf, VLAN_1), + "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24") + + input_dict = { + "r4": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure IGMP on R5 port and send IGMP join for groups " "(226.1.1.1-5)") + + intf_r5_i2 = topo["routers"]["r5"]["links"]["i2"]["interface"] + input_dict = { + "r5": {"igmp": {"interfaces": {intf_r5_i2: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = app_helper.run_join("i2", IGMP_JOIN_RANGE_1, "r5") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Enable OSPF between r1 and r2") + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + raw_config = { + dut: { + "raw_config": [ + "interface {}.{}".format(intf, VLAN_1), + "ip ospf area 0.0.0.0", + "ip ospf dead-interval 4", + "ip ospf hello-interval 1", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Start traffic from Source node") + + vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1) + result = app_helper.run_traffic("i1", IGMP_JOIN_RANGE_1, bind_intf=vlan_intf_i1_s1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + return True + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_configuring_igmp_local_join_on_reciever_dr_non_dr_nodes_p1(request): + """ + Configure IGMP local join on DR and non DR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + app_helper.stop_all_hosts() + clear_mroute(tgen) + check_router_status(tgen) + reset_config_on_routers(tgen) + clear_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure IGMP and PIM on switch connected receiver nodes") + step("Configure PIM on all upstream interfaces") + + step("Configure link between R1, R2 ,R3 and receiver on" " same vlan") + step( + "Make sure R1 is DR initially configuring highest IP on R1 and R2 " + "second highest, R1 is lower" + ) + + intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"] + intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"] + + intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"] + intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"] + + intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"] + intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"] + + vlan_input = { + "r1": { + "vlan": { + VLAN_1: [ + { + intf_r1_s1: { + "ip": SAME_VLAN_IP_1["ip"], + "subnet": SAME_VLAN_IP_1["subnet"], + } + } + ] + } + }, + "r2": { + "vlan": { + VLAN_1: [ + { + intf_r2_s1: { + "ip": SAME_VLAN_IP_2["ip"], + "subnet": SAME_VLAN_IP_2["subnet"], + } + } + ] + } + }, + "i1": { + "vlan": { + VLAN_1: [ + { + intf_i1_s1: { + "ip": SAME_VLAN_IP_4["ip"], + "subnet": SAME_VLAN_IP_4["subnet"], + } + } + ] + } + }, + } + + add_interfaces_to_vlan(tgen, vlan_input) + + raw_config = { + "r1": { + "raw_config": [ + "interface {}".format(intf_r1_s1), + "no ip address {}".format(intf_r1_s1_addr), + "no ip pim", + ] + }, + "r2": { + "raw_config": [ + "interface {}".format(intf_r2_s1), + "no ip address {}".format(intf_r2_s1_addr), + "no ip pim", + ] + }, + "i1": { + "raw_config": [ + "interface {}".format(intf_i1_s1), + "no ip address {}".format(intf_i1_s1_addr), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + raw_config = { + "r1": { + "raw_config": [ + "interface {}.{}".format(intf_r1_s1, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]), + "ip pim", + "ip igmp", + "ip igmp version 2", + ] + }, + "r2": { + "raw_config": [ + "interface {}.{}".format(intf_r2_s1, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]), + "ip pim", + "ip igmp", + "ip igmp version 2", + ] + }, + "i1": { + "raw_config": [ + "interface {}.{}".format(intf_i1_s1, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + raw_config = { + dut: { + "raw_config": [ + "interface {}.{}".format(intf, VLAN_1), + "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24") + + input_dict = { + "r4": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1 + GROUP_RANGE_3, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send IGMP local join for groups 226.1.1.1 to 226.1.1.5") + + vlan_intf_r1_s1 = "{}.{}".format(intf_r1_s1, VLAN_1) + input_dict = { + "r1": { + "igmp": { + "interfaces": { + vlan_intf_r1_s1: { + "igmp": {"version": "2", "join": IGMP_JOIN_RANGE_1} + } + } + } + } + } + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Enable OSPF between all the nodes") + + step("Configure local join on R1 for group range (227.1.1.1)") + + vlan_intf_r1_s1 = "{}.{}".format(intf_r1_s1, VLAN_1) + input_dict = { + "r1": { + "igmp": { + "interfaces": { + vlan_intf_r1_s1: { + "igmp": {"version": "2", "join": IGMP_JOIN_RANGE_3} + } + } + } + } + } + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Start traffic from R4 connected source") + + input_src = {"i2": topo["routers"]["i2"]["links"]["r5"]["interface"]} + + result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3, "r5") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("R1, R2 and R2 has IGMP groups for 226.x.x.x and 227.1.1.1 groups") + + intf_r1_s1 = "{}.{}".format( + topo["routers"]["r1"]["links"]["s1"]["interface"], VLAN_1 + ) + intf_r2_s1 = "{}.{}".format( + topo["routers"]["r2"]["links"]["s1"]["interface"], VLAN_1 + ) + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + result = verify_igmp_groups( + tgen, dut, intf, IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("R1 is DR, R2 has 226.x.x.x and 227.1.1.1 (*,G) mroute with SC flag") + step("(S,G) mroute for 226.1.1.1 group present on R2") + + source_i2 = topo["routers"]["i2"]["links"]["r5"]["ipv4"].split("/")[0] + input_dict_r2 = [ + { + "dut": "r2", + "src_address": "*", + "iif": topo["routers"]["r2"]["links"]["r4"]["interface"], + "oil": "{}.{}".format( + topo["routers"]["r2"]["links"]["s1"]["interface"], VLAN_1 + ), + } + ] + + for data in input_dict_r2: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for dut, flag in zip(["r2"], ["SC"]): + step("{} has (*,G) flag as {}".format(dut, flag)) + result = verify_multicast_flag_state(tgen, dut, "*", IGMP_JOIN_RANGE_1, flag) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Delete local join from DR node") + for _join in IGMP_JOIN_RANGE_3: + input_dict = { + "r1": { + "igmp": { + "interfaces": { + vlan_intf_r1_s1: { + "igmp": { + "join": [_join], + "delete_attr": True, + } + } + } + } + } + } + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "After removing local join 227.1.1.1 group removed from IGMP join " + "of R1, R2 node , using 'show ip igmp groups json'" + ) + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + result = verify_igmp_groups(tgen, dut, intf, IGMP_JOIN_RANGE_3, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "IGMP groups are still present \n Error: {}".format(tc_name, result) + ) + + step("(*,G) mroute for 227.1.1.1 group removed from R1 node") + step( + "After remove of local join from R1 and R2 node verify (*,G) and (S,G) " + "mroutes should not present on R1, R2 and R3 nodes" + ) + + for data in input_dict_r2: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_3, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format( + tc_name, result + ) + + step("Configure local join on R2 for group range (227.1.1.1)") + + input_dict = { + "r2": { + "igmp": { + "interfaces": { + intf_r2_s1: {"igmp": {"version": "2", "join": IGMP_JOIN_RANGE_3}} + } + } + } + } + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "After configuring local join on R2 non DR node, IGMP groups for 26.x.x.x and " + "227.1.1.1 present on all the nodes" + ) + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + result = verify_igmp_groups( + tgen, dut, intf, IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("R2 has 227.1.1.1 (*,G) mroute with SC flag") + + for dut, flag in zip(["r2"], ["SC"]): + step("{} has (*,G) flag as {}".format(dut, flag)) + result = verify_multicast_flag_state(tgen, dut, "*", IGMP_JOIN_RANGE_3, flag) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure local join on R1 for group range (227.1.1.1)") + + input_dict = { + "r1": { + "igmp": { + "interfaces": { + vlan_intf_r1_s1: { + "igmp": {"version": "2", "join": IGMP_JOIN_RANGE_3} + } + } + } + } + } + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "After configuring 227.1.1.1 on R1 node, verify no change on IGMP groups on all the nodes" + ) + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + result = verify_igmp_groups( + tgen, dut, intf, IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("R2 has 227.1.1.1 (*,G) mroute with SC flag") + + step("r2 has (*,G) flag as SC") + result = verify_multicast_flag_state(tgen, "r2", "*", IGMP_JOIN_RANGE_3, "SC") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("R1 should not have (*,G) join and (S,G) join present") + + input_dict_r1 = [ + { + "dut": "r1", + "src_address": "*", + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": "{}.{}".format( + topo["routers"]["r1"]["links"]["s1"]["interface"], VLAN_1 + ), + }, + { + "dut": "r1", + "src_address": source_i2, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": "{}.{}".format( + topo["routers"]["r1"]["links"]["s1"]["interface"], VLAN_1 + ), + }, + ] + + for data in input_dict_r1: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format( + tc_name, result + ) + + step("Remove local join from DR and Non DR node") + + input_dict = { + "r1": { + "igmp": { + "interfaces": { + vlan_intf_r1_s1: { + "igmp": { + "version": "2", + "join": IGMP_JOIN_RANGE_3, + "delete_attr": True, + } + } + } + } + }, + "r2": { + "igmp": { + "interfaces": { + intf_r2_s1: { + "igmp": { + "version": "2", + "join": IGMP_JOIN_RANGE_3, + "delete_attr": True, + } + } + } + } + }, + } + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "After remove of local join from R1 and R2 node verify (*,G) and (S,G) mroutes " + "should not present on R1, R2 nodes" + ) + + for data in input_dict_r1: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format( + tc_name, result + ) + + for data in input_dict_r2: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_static_routes_topo1.py b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_static_routes_topo1.py new file mode 100755 index 0000000..4d17da5 --- /dev/null +++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_static_routes_topo1.py @@ -0,0 +1,940 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test multicast pim sm: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: +1. Verify mroute while rebooting DR /Non DR nodes( r1, r2 , r3 on all the nodes) +""" + +import os +import sys +import json +import time +import datetime +from time import sleep +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, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + reset_config_on_routers, + apply_raw_config, + add_interfaces_to_vlan, + stop_router, + start_router, + check_router_status, + topo_daemons, + required_linux_kernel_version, +) +from lib.pim import ( + create_pim_config, + create_igmp_config, + verify_mroutes, + clear_mroute, + clear_pim_interface_traffic, + verify_pim_config, + verify_upstream_iif, + verify_multicast_flag_state, + McastTesterHelper, +) +from lib.topolog import logger +from lib.topojson import build_config_from_json + +HELLO_TIMER = 1 +HOLD_TIMER = 3 + +pytestmark = [pytest.mark.pimd] + +TOPOLOGY = """ + +Descripton: Configuring static routes on r1/r2/r3/r4/r5 for RP reachablility. +IPs are assigned automatically to routers, start IP and subnet is defined in respective JSON file +JSON snippet: + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24}, + + r5 ------- i2 + 10.0.3.2/24| 10.0.0.2/24 + | + 10.0.3.1/24| + ------------ r4 ---------- + | 10.0.1.2/24 10.0.2.2/24 | + 10.0.1.1/24 | | 10.0.2.1/24 + r1 ----------- s1 ---------- r2 + 10.0.4.2/24 | 10.0.4.3/24 + | + |10.0.4.4/24 + i1 -------- r3 + 10.0.4.1/24 + Description: + i1, i2 - FRR running iperf to send IGMP + join and traffic + r1, r2, r3, r4, r5 - FRR ruter + s1 - OVS switch +""" + +# Global variables +VLAN_1 = 2501 +GROUP_RANGE = "225.0.0.0/8" +IGMP_JOIN = "225.1.1.1" +VLAN_INTF_ADRESS_1 = "10.0.8.3/24" +SAME_VLAN_IP_1 = {"ip": "10.1.1.1", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_2 = {"ip": "10.1.1.2", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_3 = {"ip": "10.1.1.3", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_4 = {"ip": "10.1.1.4", "subnet": "255.255.255.0", "cidr": "24"} +GROUP_RANGE_1 = [ + "225.1.1.1/32", + "225.1.1.2/32", + "225.1.1.3/32", + "225.1.1.4/32", + "225.1.1.5/32", +] +IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"] +GROUP_RANGE_2 = [ + "226.1.1.1/32", + "226.1.1.2/32", + "226.1.1.3/32", + "226.1.1.4/32", + "226.1.1.5/32", +] +IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"] +GROUP_RANGE_3 = [ + "227.1.1.1/32", + "227.1.1.2/32", + "227.1.1.3/32", + "227.1.1.4/32", + "227.1.1.5/32", +] +IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"] + +intf_r1_s1 = None +intf_r1_s1_addr = None +intf_r2_s1 = None +intf_r2_s1_addr = None +intf_r3_s1 = None +intf_r3_s1_addr = None +intf_i1_s1 = None +intf_i1_s1_addr = None + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.19") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + testdir = os.path.dirname(os.path.realpath(__file__)) + json_file = "{}/pim_dr_nondr_with_static_routes_topo1.json".format(testdir) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, tgen.json_topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, tgen.json_topo) + + # XXX Replace this using "with McastTesterHelper()... " in each test if possible. + global app_helper + app_helper = McastTesterHelper(tgen) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + app_helper.cleanup() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Local APIs +# +##################################################### + + +def pre_config_for_receiver_dr_tests( + tgen, topo, tc_name, highest_priority, lowest_priority +): + """ + API to do common pre-configuration for receiver test cases + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `highest_priority`: router which will be having highest DR priority + * `lowest_priority`: router which will be having lowest DR priority + """ + + global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_r3_s1, intf_r3_s1_addr, intf_i1_s1, intf_i1_s1_addr + + step("Configure IGMP and PIM on switch connected receiver nodes") + step("Configure PIM on all upstream interfaces") + + step("Configure link between R1, R2 ,R3 and receiver on" " same vlan") + step( + "Make sure {0} is DR initially configuring highest IP on {0} and R2 " + "second highest, {1} is lower".format(highest_priority, lowest_priority) + ) + + intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"] + intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"] + + intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"] + intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"] + + intf_r3_s1 = topo["routers"]["r3"]["links"]["s1"]["interface"] + intf_r3_s1_addr = topo["routers"]["r3"]["links"]["s1"]["ipv4"] + + intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"] + intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"] + + if lowest_priority == "r1": + lowest_pr_intf = intf_r1_s1 + else: + lowest_pr_intf = intf_r3_s1 + + if highest_priority == "r1": + highest_pr_intf = intf_r1_s1 + else: + highest_pr_intf = intf_r3_s1 + + vlan_input = { + lowest_priority: { + "vlan": { + VLAN_1: [ + { + lowest_pr_intf: { + "ip": SAME_VLAN_IP_1["ip"], + "subnet": SAME_VLAN_IP_1["subnet"], + } + } + ] + } + }, + "r2": { + "vlan": { + VLAN_1: [ + { + intf_r2_s1: { + "ip": SAME_VLAN_IP_2["ip"], + "subnet": SAME_VLAN_IP_2["subnet"], + } + } + ] + } + }, + highest_priority: { + "vlan": { + VLAN_1: [ + { + highest_pr_intf: { + "ip": SAME_VLAN_IP_3["ip"], + "subnet": SAME_VLAN_IP_3["subnet"], + } + } + ] + } + }, + "i1": { + "vlan": { + VLAN_1: [ + { + intf_i1_s1: { + "ip": SAME_VLAN_IP_4["ip"], + "subnet": SAME_VLAN_IP_4["subnet"], + } + } + ] + } + }, + } + + add_interfaces_to_vlan(tgen, vlan_input) + + raw_config = { + "r1": { + "raw_config": [ + "interface {}".format(intf_r1_s1), + "no ip address {}".format(intf_r1_s1_addr), + "no ip pim", + ] + }, + "r2": { + "raw_config": [ + "interface {}".format(intf_r2_s1), + "no ip address {}".format(intf_r2_s1_addr), + "no ip pim", + ] + }, + "r3": { + "raw_config": [ + "interface {}".format(intf_r3_s1), + "no ip address {}".format(intf_r3_s1_addr), + "no ip pim", + ] + }, + "i1": { + "raw_config": [ + "interface {}".format(intf_i1_s1), + "no ip address {}".format(intf_i1_s1_addr), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + raw_config = { + lowest_priority: { + "raw_config": [ + "interface {}.{}".format(lowest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]), + "ip pim", + "ip igmp", + "ip igmp version 2", + ] + }, + "r2": { + "raw_config": [ + "interface {}.{}".format(intf_r2_s1, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]), + "ip pim", + "ip igmp", + "ip igmp version 2", + ] + }, + highest_priority: { + "raw_config": [ + "interface {}.{}".format(highest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_3["ip"], SAME_VLAN_IP_3["cidr"]), + "ip pim", + "ip igmp", + "ip igmp version 2", + ] + }, + "i1": { + "raw_config": [ + "interface {}.{}".format(intf_i1_s1, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for dut, intf in zip(["r1", "r2", "r3"], [intf_r1_s1, intf_r2_s1, intf_r3_s1]): + raw_config = { + dut: { + "raw_config": [ + "interface {}.{}".format(intf, VLAN_1), + "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24") + + input_dict = { + "r4": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send IGMP join for groups 226.1.1.1 to 226.1.1.5") + + vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1) + result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, join_intf=vlan_intf_i1_s1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Using static routes instead OSPF: Enable OSPF between all the nodes") + + step("Start traffic from R4 connected source") + + result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "r5") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + return True + + +def pre_config_for_source_dr_tests( + tgen, topo, tc_name, highest_priority, lowest_priority +): + """ + API to do common pre-configuration for source test cases + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `highest_priority`: router which will be having highest DR priority + * `lowest_priority`: router which will be having lowest DR priority + """ + + global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_r3_s1, intf_r3_s1_addr, intf_i1_s1, intf_i1_s1_addr + + step("Configure IGMP and PIM on switch connected receiver nodes") + step("Configure PIM on all upstream interfaces") + + step("Configure link between R1, R2 ,R3 and receiver on" " same vlan") + step( + "Make sure {0} is DR initially configuring highest IP on {0} and R2 " + "second highest, {1} is lower".format(highest_priority, lowest_priority) + ) + + intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"] + intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"] + + intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"] + intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"] + + intf_r3_s1 = topo["routers"]["r3"]["links"]["s1"]["interface"] + intf_r3_s1_addr = topo["routers"]["r3"]["links"]["s1"]["ipv4"] + + intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"] + intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"] + + if lowest_priority == "r1": + lowest_pr_intf = intf_r1_s1 + else: + lowest_pr_intf = intf_r3_s1 + + if highest_priority == "r1": + highest_pr_intf = intf_r1_s1 + else: + highest_pr_intf = intf_r3_s1 + + vlan_input = { + lowest_priority: { + "vlan": { + VLAN_1: [ + { + lowest_pr_intf: { + "ip": SAME_VLAN_IP_1["ip"], + "subnet": SAME_VLAN_IP_1["subnet"], + } + } + ] + } + }, + "r2": { + "vlan": { + VLAN_1: [ + { + intf_r2_s1: { + "ip": SAME_VLAN_IP_2["ip"], + "subnet": SAME_VLAN_IP_2["subnet"], + } + } + ] + } + }, + highest_priority: { + "vlan": { + VLAN_1: [ + { + highest_pr_intf: { + "ip": SAME_VLAN_IP_3["ip"], + "subnet": SAME_VLAN_IP_3["subnet"], + } + } + ] + } + }, + "i1": { + "vlan": { + VLAN_1: [ + { + intf_i1_s1: { + "ip": SAME_VLAN_IP_4["ip"], + "subnet": SAME_VLAN_IP_4["subnet"], + } + } + ] + } + }, + } + + add_interfaces_to_vlan(tgen, vlan_input) + + raw_config = { + "r1": { + "raw_config": [ + "interface {}".format(intf_r1_s1), + "no ip address {}".format(intf_r1_s1_addr), + "no ip pim", + ] + }, + "r2": { + "raw_config": [ + "interface {}".format(intf_r2_s1), + "no ip address {}".format(intf_r2_s1_addr), + "no ip pim", + ] + }, + "r3": { + "raw_config": [ + "interface {}".format(intf_r3_s1), + "no ip address {}".format(intf_r3_s1_addr), + "no ip pim", + ] + }, + "i1": { + "raw_config": [ + "interface {}".format(intf_i1_s1), + "no ip address {}".format(intf_i1_s1_addr), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Configure IGMP and PIM on switch connected receiver nodes , " + "configure PIM nbr with hello timer 1" + ) + + raw_config = { + lowest_priority: { + "raw_config": [ + "interface {}.{}".format(lowest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]), + "ip pim", + ] + }, + "r2": { + "raw_config": [ + "interface {}.{}".format(intf_r2_s1, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]), + "ip pim", + ] + }, + highest_priority: { + "raw_config": [ + "interface {}.{}".format(highest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_3["ip"], SAME_VLAN_IP_3["cidr"]), + "ip pim", + ] + }, + "i1": { + "raw_config": [ + "interface {}.{}".format(intf_i1_s1, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for dut, intf in zip(["r1", "r2", "r3"], [intf_r1_s1, intf_r2_s1, intf_r3_s1]): + raw_config = { + dut: { + "raw_config": [ + "interface {}.{}".format(intf, VLAN_1), + "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24") + + input_dict = { + "r4": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure IGMP on R5 port and send IGMP join for groups " "(226.1.1.1-5)") + + intf_r5_i2 = topo["routers"]["r5"]["links"]["i2"]["interface"] + input_dict = { + "r5": {"igmp": {"interfaces": {intf_r5_i2: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_src = {"i2": topo["routers"]["i2"]["links"]["r5"]["interface"]} + + result = app_helper.run_join("i2", IGMP_JOIN_RANGE_1, "r5") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Using static routes instead OSPF: Enable OSPF between all the nodes") + + step("Start traffic from Source node") + + vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1) + result = app_helper.run_traffic("i1", IGMP_JOIN_RANGE_1, bind_intf=vlan_intf_i1_s1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + return True + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_pim_source_dr_functionality_while_rebooting_dr_non_dr_nodes_p1(request): + """ + Verify mroute while rebooting DR /Non DR nodes( r1, r2 , r3 on all the nodes) + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + app_helper.stop_all_hosts() + clear_mroute(tgen) + check_router_status(tgen) + reset_config_on_routers(tgen) + clear_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + result = pre_config_for_source_dr_tests(tgen, topo, tc_name, "r1", "r3") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("R1 is the DR , verify using 'show ip pim interface json'") + + vlan_intf_r1_s1 = "{}.{}".format(intf_r1_s1, VLAN_1) + input_dict_dr = { + "r1": { + "pim": { + "interfaces": {vlan_intf_r1_s1: {"drAddress": SAME_VLAN_IP_3["ip"]}} + } + } + } + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "R2 is transit router for I1 to reach R4, mroute should have (s, g) mroute with " + "OIL towards R4, using 'show ip mroute json'" + ) + step( + "R2 (s, g) upstream should be in join state verify using " + "'show ip pim upstream json'" + ) + step( + "R1 has (S, G) mroute with NONE OIL and upstream as not joined, verify using " + "'show ip mroute json' 'show ip pim upstream json'" + ) + + source_i1 = SAME_VLAN_IP_4["ip"] + input_dict_r1_r2 = [ + { + "dut": "r1", + "src_address": source_i1, + "oil": "none", + "iif": "{}.{}".format( + topo["routers"]["r1"]["links"]["s1"]["interface"], VLAN_1 + ), + }, + { + "dut": "r2", + "src_address": source_i1, + "oil": topo["routers"]["r2"]["links"]["r4"]["interface"], + "iif": "{}.{}".format( + topo["routers"]["r2"]["links"]["s1"]["interface"], VLAN_1 + ), + }, + ] + + for data in input_dict_r1_r2: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + if data["dut"] == "r2": + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + else: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Upstream is still joined state \n Error: {}".format(tc_name, result) + ) + + step("Reboot R3 node") + stop_router(tgen, "r3") + + step( + "After reboot of R3 verify R1 continues to be DR, using 'show ip pim interface json'" + ) + + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("R3 should not have any mroute and upstream") + step("R2 has mroute with OIL towards R4 /R1 , verify using 'show ip mroute'") + step( + "R2 has upstream with Join RejP state verify using 'show ip pim upstream json'" + ) + step("R1 has mroute with none OIL and upstream with Not Join") + + for data in input_dict_r1_r2: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + if data["dut"] == "r2": + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + else: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Upstream is still joined state \n Error: {}".format(tc_name, result) + ) + + step("Reboot R2 node") + stop_router(tgen, "r2") + + step( + "After reboot of R2, R1 continues to be DR verify using 'show ip pim interface json'" + ) + + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "R3 and R2 should not have any mroute and upstream , verify using " + "'show ip mroute json' 'show ip pim upstream json'" + ) + step("R1 has mroute created with empty OIL, using 'show ip mroute json'") + step( + "R1 has upstream with Not Join, Rej Prune , verify using 'show ip pim upstream json'" + ) + + for data in input_dict_r1_r2: + if data["dut"] == "r1": + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Upstream is still joined state \n Error: {}".format(tc_name, result) + ) + + step("Reboot R1 node using FRR stop") + stop_router(tgen, "r1") + + step( + "After stop of all the routers, verify upstream and mroutes should " + "not present in any of them" + ) + + for data in input_dict_r1_r2: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format( + tc_name, result + ) + + step("start FRR for all the nodes") + start_router(tgen, "r1") + start_router(tgen, "r2") + start_router(tgen, "r3") + + step("After start of all the routers, R1 became DR") + + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_r1_r2: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + if data["dut"] == "r2": + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py new file mode 100755 index 0000000..a5d2730 --- /dev/null +++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py @@ -0,0 +1,829 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test multicast pim sm: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: +1. Verify mroutes when transit router present between RP and Source DR +""" + +import os +import sys +import json +import time +import datetime +from time import sleep +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, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + reset_config_on_routers, + apply_raw_config, + add_interfaces_to_vlan, + check_router_status, + topo_daemons, + required_linux_kernel_version, +) +from lib.pim import ( + create_pim_config, + create_igmp_config, + verify_mroutes, + clear_mroute, + clear_pim_interface_traffic, + verify_pim_config, + verify_upstream_iif, + verify_multicast_traffic, + McastTesterHelper, +) +from lib.topolog import logger +from lib.topojson import build_config_from_json + +HELLO_TIMER = 1 +HOLD_TIMER = 3 + +pytestmark = [pytest.mark.pimd] + +TOPOLOGY = """ + +Descripton: Configuring OSPF on r1/r2/r4/r5/r6 for RP reachablility, We have r6 as a transit router between + r1/r2 and r4. +IPs are assigned automatically to routers, start IP and subnet is defined in respective JSON file +JSON snippet: + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24}, + + r5 ------- i2 + 10.0.3.2/24| 10.0.0.2/24 + | + 10.0.3.1/24| + r4 + |10.0.4.1/24 + | + 10.0.4.2/24| + ------------ r6 ---------- + | 10.0.1.2/24 10.0.2.2/24 | + 10.0.1.1/24 | | 10.0.2.1/24 + r1 ----------- s1 ---------- r2 + 10.0.5.2/24 | 10.0.5.3/24 + | + |10.0.5.1/24 + i1 + + + + Description: + i1, i2 - FRR running iperf to send IGMP + join and traffic + r1, r2, r4, r5, r6 - FRR ruter + s1 - OVS switch +""" + +# Global variables +VLAN_1 = 2501 +GROUP_RANGE = "225.0.0.0/8" +IGMP_JOIN = "225.1.1.1" +VLAN_INTF_ADRESS_1 = "10.0.8.3/24" +SAME_VLAN_IP_1 = {"ip": "10.1.1.1", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_2 = {"ip": "10.1.1.2", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_3 = {"ip": "10.1.1.3", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_4 = {"ip": "10.1.1.4", "subnet": "255.255.255.0", "cidr": "24"} +GROUP_RANGE_1 = [ + "225.1.1.1/32", + "225.1.1.2/32", + "225.1.1.3/32", + "225.1.1.4/32", + "225.1.1.5/32", +] +IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"] +GROUP_RANGE_2 = [ + "226.1.1.1/32", + "226.1.1.2/32", + "226.1.1.3/32", + "226.1.1.4/32", + "226.1.1.5/32", +] +IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"] +GROUP_RANGE_3 = [ + "227.1.1.1/32", + "227.1.1.2/32", + "227.1.1.3/32", + "227.1.1.4/32", + "227.1.1.5/32", +] +IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"] + +intf_r1_s1 = None +intf_r1_s1_addr = None +intf_r2_s1 = None +intf_r2_s1_addr = None +intf_r3_s1 = None +intf_r3_s1_addr = None +intf_i1_s1 = None +intf_i1_s1_addr = None + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.19") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + testdir = os.path.dirname(os.path.realpath(__file__)) + json_file = "{}/pim_dr_nondr_with_transit_router_topo3.json".format(testdir) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, tgen.json_topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, tgen.json_topo) + + # XXX Replace this using "with McastTesterHelper()... " in each test if possible. + global app_helper + app_helper = McastTesterHelper(tgen) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + app_helper.cleanup() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Local APIs +# +##################################################### + + +def pre_config_for_receiver_dr_tests( + tgen, topo, tc_name, highest_priority, lowest_priority +): + """ + API to do common pre-configuration for receiver test cases + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `highest_priority`: router which will be having highest DR priority + * `lowest_priority`: router which will be having lowest DR priority + """ + + global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_i1_s1, intf_i1_s1_addr + + step("Configure IGMP and PIM on switch connected receiver nodes") + step("Configure PIM on all upstream interfaces") + + step("Configure link between R1, R2 ,R3 and receiver on" " same vlan") + step( + "Make sure {0} is DR initially configuring highest IP on {0} and R2 " + "second highest, {1} is lower".format(highest_priority, lowest_priority) + ) + + intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"] + intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"] + + intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"] + intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"] + + intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"] + intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"] + + if lowest_priority == "r1": + lowest_pr_intf = intf_r1_s1 + else: + lowest_pr_intf = intf_r2_s1 + + if highest_priority == "r1": + highest_pr_intf = intf_r1_s1 + else: + highest_pr_intf = intf_r2_s1 + + vlan_input = { + lowest_priority: { + "vlan": { + VLAN_1: [ + { + lowest_pr_intf: { + "ip": SAME_VLAN_IP_1["ip"], + "subnet": SAME_VLAN_IP_1["subnet"], + } + } + ] + } + }, + highest_priority: { + "vlan": { + VLAN_1: [ + { + highest_pr_intf: { + "ip": SAME_VLAN_IP_2["ip"], + "subnet": SAME_VLAN_IP_2["subnet"], + } + } + ] + } + }, + "i1": { + "vlan": { + VLAN_1: [ + { + intf_i1_s1: { + "ip": SAME_VLAN_IP_4["ip"], + "subnet": SAME_VLAN_IP_4["subnet"], + } + } + ] + } + }, + } + + add_interfaces_to_vlan(tgen, vlan_input) + + raw_config = { + "r1": { + "raw_config": [ + "interface {}".format(intf_r1_s1), + "no ip address {}".format(intf_r1_s1_addr), + "no ip pim", + ] + }, + "r2": { + "raw_config": [ + "interface {}".format(intf_r2_s1), + "no ip address {}".format(intf_r2_s1_addr), + "no ip pim", + ] + }, + "i1": { + "raw_config": [ + "interface {}".format(intf_i1_s1), + "no ip address {}".format(intf_i1_s1_addr), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + raw_config = { + lowest_priority: { + "raw_config": [ + "interface {}.{}".format(lowest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]), + "ip pim", + "ip igmp", + "ip igmp version 2", + ] + }, + highest_priority: { + "raw_config": [ + "interface {}.{}".format(highest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]), + "ip pim", + "ip igmp", + "ip igmp version 2", + ] + }, + "i1": { + "raw_config": [ + "interface {}.{}".format(intf_i1_s1, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + raw_config = { + dut: { + "raw_config": [ + "interface {}.{}".format(intf, VLAN_1), + "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24") + + input_dict = { + "r4": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send IGMP join for groups 226.1.1.1 to 226.1.1.5") + + vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1) + result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, join_intf=vlan_intf_i1_s1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Enable OSPF between r1 and r2") + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + raw_config = { + dut: { + "raw_config": [ + "interface {}.{}".format(intf, VLAN_1), + "ip ospf area 0.0.0.0", + "ip ospf dead-interval 4", + "ip ospf hello-interval 1", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Start traffic from R4 connected source") + + result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "r5") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + return True + + +def pre_config_for_source_dr_tests( + tgen, topo, tc_name, highest_priority, lowest_priority +): + """ + API to do common pre-configuration for source test cases + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `highest_priority`: router which will be having highest DR priority + * `lowest_priority`: router which will be having lowest DR priority + """ + + global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_i1_s1, intf_i1_s1_addr + + step("Configure IGMP and PIM on switch connected receiver nodes") + step("Configure PIM on all upstream interfaces") + + step("Configure link between R1, R2 ,R3 and receiver on" " same vlan") + step( + "Make sure {0} is DR initially configuring highest IP on {0} and R2 " + "second highest, {1} is lower".format(highest_priority, lowest_priority) + ) + + intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"] + intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"] + + intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"] + intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"] + + intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"] + intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"] + + if lowest_priority == "r1": + lowest_pr_intf = intf_r1_s1 + else: + lowest_pr_intf = intf_r2_s1 + + if highest_priority == "r1": + highest_pr_intf = intf_r1_s1 + else: + highest_pr_intf = intf_r2_s1 + + vlan_input = { + lowest_priority: { + "vlan": { + VLAN_1: [ + { + lowest_pr_intf: { + "ip": SAME_VLAN_IP_1["ip"], + "subnet": SAME_VLAN_IP_1["subnet"], + } + } + ] + } + }, + highest_priority: { + "vlan": { + VLAN_1: [ + { + highest_pr_intf: { + "ip": SAME_VLAN_IP_2["ip"], + "subnet": SAME_VLAN_IP_2["subnet"], + } + } + ] + } + }, + "i1": { + "vlan": { + VLAN_1: [ + { + intf_i1_s1: { + "ip": SAME_VLAN_IP_4["ip"], + "subnet": SAME_VLAN_IP_4["subnet"], + } + } + ] + } + }, + } + + add_interfaces_to_vlan(tgen, vlan_input) + + raw_config = { + "r1": { + "raw_config": [ + "interface {}".format(intf_r1_s1), + "no ip address {}".format(intf_r1_s1_addr), + "no ip pim", + ] + }, + "r2": { + "raw_config": [ + "interface {}".format(intf_r2_s1), + "no ip address {}".format(intf_r2_s1_addr), + "no ip pim", + ] + }, + "i1": { + "raw_config": [ + "interface {}".format(intf_i1_s1), + "no ip address {}".format(intf_i1_s1_addr), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Configure IGMP and PIM on switch connected receiver nodes , " + "configure PIM nbr with hello timer 1" + ) + + raw_config = { + lowest_priority: { + "raw_config": [ + "interface {}.{}".format(lowest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]), + "ip pim", + ] + }, + highest_priority: { + "raw_config": [ + "interface {}.{}".format(highest_pr_intf, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]), + "ip pim", + ] + }, + "i1": { + "raw_config": [ + "interface {}.{}".format(intf_i1_s1, VLAN_1), + "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + raw_config = { + dut: { + "raw_config": [ + "interface {}.{}".format(intf, VLAN_1), + "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24") + + input_dict = { + "r4": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure IGMP on R5 port and send IGMP join for groups " "(226.1.1.1-5)") + + intf_r5_i2 = topo["routers"]["r5"]["links"]["i2"]["interface"] + input_dict = { + "r5": {"igmp": {"interfaces": {intf_r5_i2: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = app_helper.run_join("i2", IGMP_JOIN_RANGE_1, "r5") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Enable OSPF between r1 and r2") + + for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]): + raw_config = { + dut: { + "raw_config": [ + "interface {}.{}".format(intf, VLAN_1), + "ip ospf area 0.0.0.0", + "ip ospf dead-interval 4", + "ip ospf hello-interval 1", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Start traffic from Source node") + + vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1) + result = app_helper.run_traffic("i1", IGMP_JOIN_RANGE_1, bind_intf=vlan_intf_i1_s1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + return True + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_mroute_when_transit_router_present_between_rp_and_source_dr_p1(request): + """ + Verify mroutes when transit router present between RP and Source DR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + app_helper.stop_all_hosts() + clear_mroute(tgen) + check_router_status(tgen) + reset_config_on_routers(tgen) + clear_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + result = pre_config_for_source_dr_tests(tgen, topo, tc_name, "r1", "r2") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Taken care in base config: Add router R6 between RP and R1 , R2") + + step("R1 is the DR, mroute and upstream created on R1") + + vlan_intf_r1_s1 = "{}.{}".format(intf_r1_s1, VLAN_1) + input_dict_dr = { + "r1": { + "pim": { + "interfaces": {vlan_intf_r1_s1: {"drAddress": SAME_VLAN_IP_2["ip"]}} + } + } + } + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("On R1 Mroute OIL towards R6, upstream in join Rej Prune state") + + source_i1 = SAME_VLAN_IP_4["ip"] + input_dict_r1 = [ + { + "dut": "r1", + "src_address": source_i1, + "oil": topo["routers"]["r1"]["links"]["r6"]["interface"], + "iif": "{}.{}".format( + topo["routers"]["r1"]["links"]["s1"]["interface"], VLAN_1 + ), + } + ] + + for data in input_dict_r1: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "R5 have mroute created and traffic is received, verify using " + "'show ip mroute json' 'show ip multicast json'" + ) + + input_dict_r5 = [ + { + "dut": "r5", + "src_address": "*", + "iif": topo["routers"]["r5"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r5"]["links"]["i2"]["interface"], + }, + { + "dut": "r5", + "src_address": source_i1, + "iif": topo["routers"]["r5"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r5"]["links"]["i2"]["interface"], + }, + ] + + for data in input_dict_r5: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + input_dict_traffic_r5 = { + "r5": { + "traffic_received": [topo["routers"]["r5"]["links"]["r4"]["interface"]], + "traffic_sent": [topo["routers"]["r5"]["links"]["i2"]["interface"]], + } + } + + result = verify_multicast_traffic(tgen, input_dict_traffic_r5) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("Make R2 as DR configuring higher priority value") + + raw_config = { + "r1": { + "raw_config": [ + "interface {}.{}".format(intf_r1_s1, VLAN_1), + "no ip address {}/{}".format( + SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_1["cidr"] + ), + "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]), + ] + }, + "r2": { + "raw_config": [ + "interface {}.{}".format(intf_r2_s1, VLAN_1), + "no ip address {}/{}".format( + SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"] + ), + "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]), + ] + }, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("R2 is the DR, mroute and upstream created on R2") + + vlan_intf_r2_s1 = "{}.{}".format(intf_r2_s1, VLAN_1) + input_dict_dr = { + "r2": { + "pim": { + "interfaces": {vlan_intf_r2_s1: {"drAddress": SAME_VLAN_IP_2["ip"]}} + } + } + } + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "R5 have mroute created and traffic is received, verify using " + "'show ip mroute json' 'show ip multicast json'" + ) + + for data in input_dict_r5: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_multicast_traffic(tgen, input_dict_traffic_r5) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) |