diff options
author | Rafael Zalamena <rzalamena@opensourcerouting.org> | 2021-04-20 19:54:27 +0200 |
---|---|---|
committer | Rafael Zalamena <rzalamena@gmail.com> | 2021-06-09 17:32:24 +0200 |
commit | 1771900c0266b9b257dd642f0f680a1e38925f25 (patch) | |
tree | 8e768110053d7a3baf7453a694a21ab5b634721e /tests | |
parent | topotests: support adding hosts (diff) | |
download | frr-1771900c0266b9b257dd642f0f680a1e38925f25.tar.xz frr-1771900c0266b9b257dd642f0f680a1e38925f25.zip |
topotests: new test topology for MSDP
Add basic topology test for MSDP meshed groups.
Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
Diffstat (limited to 'tests')
17 files changed, 643 insertions, 0 deletions
diff --git a/tests/topotests/lib/mcast-tester.py b/tests/topotests/lib/mcast-tester.py new file mode 100755 index 000000000..07e4ab877 --- /dev/null +++ b/tests/topotests/lib/mcast-tester.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + +""" +Subscribe to a multicast group so that the kernel sends an IGMP JOIN +for the multicast group we subscribed to. +""" + +import argparse +import os +import json +import socket +import subprocess +import struct +import sys +import time + +# +# Functions +# +def interface_name_to_index(name): + "Gets the interface index using its name. Returns None on failure." + interfaces = json.loads( + subprocess.check_output('ip -j link show', shell=True)) + + for interface in interfaces: + if interface['ifname'] == name: + return interface['ifindex'] + + return None + + +def multicast_join(sock, ifindex, group, port): + "Joins a multicast group." + mreq = struct.pack( + "=4sLL", socket.inet_aton(args.group), socket.INADDR_ANY, ifindex + ) + + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind((group, port)) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + + +# +# Main code. +# +parser = argparse.ArgumentParser(description="Multicast RX utility") +parser.add_argument('socket', help='Point to topotest UNIX socket') +parser.add_argument('group', help='Multicast IP') +parser.add_argument('interface', help='Interface name') +parser.add_argument( + '--send', + help='Transmit instead of join with interval (defaults to 0.7 sec)', + type=float, default=0) +args = parser.parse_args() + +ttl = 16 +port = 1000 + +# Get interface index/validate. +ifindex = interface_name_to_index(args.interface) +if ifindex is None: + sys.stderr.write('Interface {} does not exists\n'.format(args.interface)) + sys.exit(1) + +# We need root privileges to set up multicast. +if os.geteuid() != 0: + sys.stderr.write("ERROR: You must have root privileges\n") + sys.exit(1) + +# Wait for topotest to synchronize with us. +toposock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) +while True: + try: + toposock.connect(args.socket) + break + except ConnectionRefusedError: + time.sleep(1) + continue + +msock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +if args.send > 0: + # Prepare multicast bit in that interface. + msock.setsockopt( + socket.SOL_SOCKET, 25, + struct.pack("%ds" % len(args.interface), + args.interface.encode('utf-8'))) + # Set packets TTL. + msock.setsockopt( + socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack("b", ttl)) + # Block to ensure packet send. + msock.setblocking(True) + # Set topotest socket non blocking so we can multiplex the main loop. + toposock.setblocking(False) +else: + multicast_join(msock, ifindex, args.group, port) + +counter = 0 +while True: + if args.send > 0: + msock.sendto(b"test %d" % counter, (args.group, port)) + counter += 1 + time.sleep(args.send) + + try: + data = toposock.recv(1) + if data == b'': + print(' -> Connection closed') + break + except BlockingIOError: + continue + +msock.close() + +sys.exit(0) diff --git a/tests/topotests/msdp_mesh_topo1/__init__.py b/tests/topotests/msdp_mesh_topo1/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/__init__.py diff --git a/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf new file mode 100644 index 000000000..953d90aa0 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65000 + neighbor 10.254.254.2 remote-as 65000 + neighbor 10.254.254.2 update-source 10.254.254.1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf new file mode 100644 index 000000000..c1adbd544 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf @@ -0,0 +1,8 @@ +interface r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 192.168.1.0/24 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/msdp_mesh_topo1/r1/pimd.conf b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf new file mode 100644 index 000000000..49341efa5 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf @@ -0,0 +1,15 @@ +interface lo + ip pim + ip pim use-source 10.254.254.1 +! +interface r1-eth0 + ip pim +! +interface r1-eth1 + ip pim + ip igmp +! +ip pim rp 10.254.254.1 +ip msdp mesh-group mg-1 source 10.254.254.1 +ip msdp mesh-group mg-1 member 10.254.254.2 +ip msdp mesh-group mg-1 member 10.254.254.3 diff --git a/tests/topotests/msdp_mesh_topo1/r1/zebra.conf b/tests/topotests/msdp_mesh_topo1/r1/zebra.conf new file mode 100644 index 000000000..42c850f00 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/zebra.conf @@ -0,0 +1,11 @@ +ip forwarding +! +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ip address 192.168.1.2/24 +! +interface r1-eth1 + ip address 192.168.10.1/24 +! diff --git a/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf new file mode 100644 index 000000000..f442efc60 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65000 + neighbor pg-1 peer-group + neighbor pg-1 update-source 10.254.254.1 + neighbor pg-1 remote-as 65000 + neighbor 10.254.254.1 peer-group pg-1 + neighbor 10.254.254.3 peer-group pg-1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf new file mode 100644 index 000000000..9e9ac5fb2 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf @@ -0,0 +1,13 @@ +interface r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +interface r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 192.168.1.0/24 area 0.0.0.0 + network 192.168.2.0/24 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/msdp_mesh_topo1/r2/pimd.conf b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf new file mode 100644 index 000000000..9005263ed --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf @@ -0,0 +1,14 @@ +interface lo + ip pim + ip pim use-source 10.254.254.2 +! +interface r2-eth0 + ip pim +! +interface r2-eth1 + ip pim +! +ip pim rp 10.254.254.2 +ip msdp mesh-group mg-1 source 10.254.254.2 +ip msdp mesh-group mg-1 member 10.254.254.1 +ip msdp mesh-group mg-1 member 10.254.254.3 diff --git a/tests/topotests/msdp_mesh_topo1/r2/zebra.conf b/tests/topotests/msdp_mesh_topo1/r2/zebra.conf new file mode 100644 index 000000000..6b2619421 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/zebra.conf @@ -0,0 +1,11 @@ +ip forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ip address 192.168.1.1/24 +! +interface r2-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf new file mode 100644 index 000000000..6c3f89ad9 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65000 + neighbor 192.168.2.1 remote-as 65000 + neighbor 192.168.2.1 update-source 10.254.254.3 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf new file mode 100644 index 000000000..7b7b1abe6 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf @@ -0,0 +1,8 @@ +interface r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 192.168.2.0/24 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/msdp_mesh_topo1/r3/pimd.conf b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf new file mode 100644 index 000000000..30e114856 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf @@ -0,0 +1,15 @@ +interface lo + ip pim + ip pim use-source 10.254.254.3 +! +interface r3-eth0 + ip pim +! +interface r3-eth1 + ip pim + ip igmp +! +ip pim rp 10.254.254.3 +ip msdp mesh-group mg-1 source 10.254.254.3 +ip msdp mesh-group mg-1 member 10.254.254.1 +ip msdp mesh-group mg-1 member 10.254.254.2 diff --git a/tests/topotests/msdp_mesh_topo1/r3/zebra.conf b/tests/topotests/msdp_mesh_topo1/r3/zebra.conf new file mode 100644 index 000000000..a8a15f3c0 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/zebra.conf @@ -0,0 +1,11 @@ +ip forwarding +! +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 192.168.2.2/24 +! +interface r3-eth1 + ip address 192.168.30.1/24 +! diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot new file mode 100644 index 000000000..8792e2c7b --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot @@ -0,0 +1,88 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="msdp_mesh_topo1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + h1 [ + shape=doubleoctagon + label="h1", + fillcolor="#4f4f4f", + style=filled, + ]; + h2 [ + shape=doubleoctagon + label="h2", + fillcolor="#4f4f4f", + style=filled, + ]; + + # Switches + s1 [ + shape=oval, + label="sw1\n192.168.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="sw2\n192.168.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s3 [ + shape=oval, + label="sw3\n192.168.10.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s4 [ + shape=oval, + label="sw3\n192.168.30.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- s1 [label="eth0\n.2"]; + r2 -- s1 [label="eth0\n.1"]; + + r2 -- s2 [label="eth1\n.1"]; + r3 -- s2 [label="eth0\n.2"]; + + r1 -- s3 [label="eth1\n.1"]; + h1 -- s3 [label="eth0\n.2"]; + + r3 -- s4 [label="eth1\n.1"]; + h2 -- s4 [label="eth0\n.2"]; +} diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png Binary files differnew file mode 100644 index 000000000..9a15b8b08 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py new file mode 100644 index 000000000..719ead091 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python + +# +# test_msdp_mesh_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (C) 2021 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# 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 NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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. +# + +""" +test_msdp_mesh_topo1.py: Test the FRR PIM MSDP mesh groups. +""" + +import os +import sys +import json +from functools import partial +import pytest +import socket + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd, pytest.mark.pimd] + +# +# Test global variables: +# They are used to handle communicating with external application. +# +APP_SOCK_PATH = '/tmp/topotests/apps.sock' +HELPER_APP_PATH = os.path.join(CWD, "../lib/mcast-tester.py") +app_listener = None +app_clients = {} + +def listen_to_applications(): + "Start listening socket to connect with applications." + # Remove old socket. + try: + os.unlink(APP_SOCK_PATH) + except OSError: + pass + + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) + sock.bind(APP_SOCK_PATH) + sock.listen(10) + global app_listener + app_listener = sock + +def accept_host(host): + "Accept connection from application running in hosts." + global app_listener, app_clients + conn = app_listener.accept() + app_clients[host] = { + 'fd': conn[0], + 'address': conn[1] + } + +def close_applications(): + "Signal applications to stop and close all sockets." + global app_listener, app_clients + + # Close listening socket. + app_listener.close() + + # Remove old socket. + try: + os.unlink(APP_SOCK_PATH) + except OSError: + pass + + # Close all host connections. + for host in ["h1", "h2"]: + if app_clients.get(host) is None: + continue + app_clients["h1"]["fd"].close() + + +class MSDPMeshTopo1(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 3 routers + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + # Create stub networks for multicast traffic. + tgen.add_host("h1", "192.168.10.2/24", "192.168.10.1") + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["h1"]) + + tgen.add_host("h2", "192.168.30.2/24", "192.168.30.1") + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["h2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(MSDPMeshTopo1, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + daemon_file = "{}/{}/bgpd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BGP, daemon_file) + + daemon_file = "{}/{}/ospfd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_OSPF, daemon_file) + + daemon_file = "{}/{}/pimd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_PIM, daemon_file) + + # Initialize all routers. + tgen.start_router() + + # Start applications socket. + listen_to_applications() + + +def test_wait_ospf_convergence(): + "Wait for OSPF to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_loopback_route(router, iptype, route, proto): + "Wait until route is present on RIB for protocol." + logger.info("waiting route {} in {}".format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show {} route json".format(iptype), + {route: [{"protocol": proto}]} + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=1) + assertmsg = '"{}" OSPF convergence failure'.format(router) + assert result is None, assertmsg + + # Wait for R1 <-> R2 convergence. + expect_loopback_route("r1", "ip", "10.254.254.2/32", "ospf") + # Wait for R1 <-> R3 convergence. + expect_loopback_route("r1", "ip", "10.254.254.3/32", "ospf") + + # Wait for R2 <-> R1 convergence. + expect_loopback_route("r2", "ip", "10.254.254.1/32", "ospf") + # Wait for R2 <-> R3 convergence. + expect_loopback_route("r2", "ip", "10.254.254.3/32", "ospf") + + # Wait for R3 <-> R1 convergence. + expect_loopback_route("r3", "ip", "10.254.254.1/32", "ospf") + # Wait for R3 <-> R2 convergence. + expect_loopback_route("r3", "ip", "10.254.254.2/32", "ospf") + + +def test_wait_msdp_convergence(): + "Wait for MSDP to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test MSDP convergence") + + tgen.gears["h1"].run("{} --send='0.7' '{}' '{}' '{}' &".format( + HELPER_APP_PATH, APP_SOCK_PATH, '229.0.1.10', 'h1-eth0')) + accept_host("h1") + + tgen.gears["h2"].run("{} '{}' '{}' '{}' &".format( + HELPER_APP_PATH, APP_SOCK_PATH, '229.0.1.10', 'h2-eth0')) + accept_host("h2") + + def expect_msdp_peer(router, peer, sa_count=0): + "Expect MSDP peer connection to be established with SA amount." + logger.info("waiting MSDP connection from peer {} on router {}".format(peer, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ip msdp peer json", + {peer: {"state": "established", "saCount": sa_count}} + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" MSDP connection failure'.format(router) + assert result is None, assertmsg + + # R1 peers. + expect_msdp_peer("r1", "10.254.254.2") + expect_msdp_peer("r1", "10.254.254.3") + + # R2 peers. + expect_msdp_peer("r2", "10.254.254.1", 1) + expect_msdp_peer("r2", "10.254.254.3") + + # R3 peers. + expect_msdp_peer("r3", "10.254.254.1", 1) + expect_msdp_peer("r3", "10.254.254.2") + + +def test_msdp_sa_configuration(): + "Expect the multicast traffic SA to be created" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test MSDP SA") + + def expect_msdp_sa(router, source, group, local, rp, spt_setup): + "Expect MSDP SA." + logger.info("waiting MSDP SA on router {}".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ip msdp sa json", + {group: {source: {"local": local, "rp": rp, "sptSetup": spt_setup}}} + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" MSDP SA failure'.format(router) + assert result is None, assertmsg + + source = "192.168.10.2" + group = "229.0.1.10" + rp = "10.254.254.1" + + # R1 SA. + expect_msdp_sa("r1", source, group, "yes", "-", "-") + + # R2 SA. + expect_msdp_sa("r2", source, group, "no", rp, "no") + + # R3 peers. + expect_msdp_sa("r3", source, group, "no", rp, "yes") + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + close_applications() + tgen.stop_topology() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) |