]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/pim_acl/test_pim_acl.py
*: auto-convert to SPDX License IDs
[mirror_frr.git] / tests / topotests / pim_acl / test_pim_acl.py
1 #!/usr/bin/env python
2 # SPDX-License-Identifier: ISC
3
4 #
5 # test_pim_acl.py
6 # Part of NetDEF Topology Tests
7 #
8 # Copyright (c) 2020 by
9 # Network Device Education Foundation, Inc. ("NetDEF")
10 #
11
12 """
13 test_pim_acl.py: Test PIM with RP selection using ACLs
14 """
15
16 # Test PIM RP selection with ACLs
17 #
18 # Testing RP selection with ACLs. R1 uses multiple ACLs
19 # to select desired RPs (R11 to R15)
20 #
21 # Test steps:
22 # - setup_module()
23 # Create topology. Hosts are only using zebra/staticd,
24 # no PIM, no OSPF (using IGMPv2 for multicast)
25 # - test_ospf_convergence()
26 # Wait for OSPF convergence in each VRF. OSPF is run on
27 # R1 and R11 - R15.
28 # - test_pim_convergence()
29 # Wait for PIM convergence on all routers. PIM is run on
30 # R1 and R11 - R15.
31 # - test_mcast_acl_1():
32 # Test 1st ACL entry 239.100.0.0/28 with 239.100.0.1 which
33 # should use R11 as RP
34 # Stop multicast after verification
35 # - test_mcast_acl_2():
36 # Test 2nd ACL entry 239.100.0.17/32 with 239.100.0.17 which
37 # should use R12 as RP
38 # Stop multicast after verification
39 # - test_mcast_acl_3():
40 # Test 3rd ACL entry 239.100.0.32/27 with 239.100.0.32 which
41 # should use R13 as RP
42 # Stop multicast after verification
43 # - test_mcast_acl_4():
44 # Test 4th ACL entry 239.100.0.128/25 with 239.100.0.255 which
45 # should use R14 as RP
46 # Stop multicast after verification
47 # - test_mcast_acl_5():
48 # Test 5th ACL entry 239.100.0.96/28 with 239.100.0.97 which
49 # should use R14 as RP
50 # Stop multicast after verification
51 # - test_mcast_acl_6():
52 # Test 6th ACL entry 239.100.0.64/28 with 239.100.0.70 which
53 # should use R15 as RP
54 # Stop multicast after verification
55 # - teardown_module()
56 # shutdown topology
57 #
58
59 # XXX clean up in later commit to avoid conflict on rebase
60 # pylint: disable=C0413
61 TOPOLOGY = """
62 +----------+
63 | Host H2 |
64 | Source |
65 +----------+
66 .2 |
67 +-----------+ | +----------+
68 | | .1 | .11 | Host R11 |
69 +---------+ | R1 |---------+--------| PIM RP |
70 | Host H1 | 192.168.100.0/24 | | 192.168.101.0/24 +----------+
71 | receive |------------------| uses ACLs | | +----------+
72 |IGMP JOIN| .10 .1 | to pick | | .12 | Host R12 |
73 +---------+ | RP | +--------| PIM RP |
74 | | | +----------+
75 +-----------+ | +----------+
76 | .13 | Host R13 |
77 +--------| PIM RP |
78 | +----------+
79 | +----------+
80 | .14 | Host R14 |
81 +--------| PIM RP |
82 | +----------+
83 | +----------+
84 | .15 | Host R15 |
85 +--------| PIM RP |
86 +----------+
87 """
88
89 import json
90 import functools
91 import os
92 import sys
93 import pytest
94
95 # Save the Current Working Directory to find configuration files.
96 CWD = os.path.dirname(os.path.realpath(__file__))
97 sys.path.append(os.path.join(CWD, "../"))
98
99 # pylint: disable=C0413
100 # Import topogen and topotest helpers
101 from lib import topotest
102 from lib.topogen import Topogen, TopoRouter, get_topogen
103 from lib.topolog import logger
104
105 # Required to instantiate the topology builder class.
106 from lib.pim import McastTesterHelper
107
108 pytestmark = [pytest.mark.pimd, pytest.mark.ospfd]
109
110
111 def build_topo(tgen):
112 for hostNum in range(1, 3):
113 tgen.add_router("h{}".format(hostNum))
114
115 # Create the main router
116 tgen.add_router("r1")
117
118 # Create the PIM RP routers
119 for rtrNum in range(11, 16):
120 tgen.add_router("r{}".format(rtrNum))
121
122 # Setup Switches and connections
123 for swNum in range(1, 3):
124 tgen.add_switch("sw{}".format(swNum))
125
126 # Add connections H1 to R1 switch sw1
127 tgen.gears["h1"].add_link(tgen.gears["sw1"])
128 tgen.gears["r1"].add_link(tgen.gears["sw1"])
129
130 # Add connections R1 to R1x switch sw2
131 tgen.gears["r1"].add_link(tgen.gears["sw2"])
132 tgen.gears["h2"].add_link(tgen.gears["sw2"])
133 tgen.gears["r11"].add_link(tgen.gears["sw2"])
134 tgen.gears["r12"].add_link(tgen.gears["sw2"])
135 tgen.gears["r13"].add_link(tgen.gears["sw2"])
136 tgen.gears["r14"].add_link(tgen.gears["sw2"])
137 tgen.gears["r15"].add_link(tgen.gears["sw2"])
138
139
140 #####################################################
141 #
142 # Tests starting
143 #
144 #####################################################
145
146
147 def setup_module(module):
148 logger.info("PIM RP ACL Topology: \n {}".format(TOPOLOGY))
149
150 tgen = Topogen(build_topo, module.__name__)
151 tgen.start_topology()
152
153 # Starting Routers
154 router_list = tgen.routers()
155
156 for rname, router in router_list.items():
157 logger.info("Loading router %s" % rname)
158 router.load_config(
159 TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
160 )
161 if rname[0] != "h":
162 # Only load ospf on routers, not on end hosts
163 router.load_config(
164 TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
165 )
166 router.load_config(
167 TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname))
168 )
169 tgen.start_router()
170
171
172 def teardown_module(module):
173 tgen = get_topogen()
174 tgen.stop_topology()
175
176
177 def test_ospf_convergence():
178 "Test for OSPFv2 convergence"
179 tgen = get_topogen()
180
181 # Skip if previous fatal error condition is raised
182 if tgen.routers_have_failure():
183 pytest.skip(tgen.errors)
184
185 logger.info("Checking OSPFv2 convergence on router r1")
186
187 router = tgen.gears["r1"]
188 reffile = os.path.join(CWD, "r1/ospf_neighbor.json")
189 expected = json.loads(open(reffile).read())
190
191 test_func = functools.partial(
192 topotest.router_json_cmp, router, "show ip ospf neighbor json", expected
193 )
194 _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
195 assertmsg = "OSPF router R1 did not converge"
196 assert res is None, assertmsg
197
198
199 def test_pim_convergence():
200 "Test for PIM convergence"
201 tgen = get_topogen()
202
203 # Skip if previous fatal error condition is raised
204 if tgen.routers_have_failure():
205 pytest.skip(tgen.errors)
206
207 logger.info("Checking PIM convergence on router r1")
208
209 router = tgen.gears["r1"]
210 reffile = os.path.join(CWD, "r1/pim_neighbor.json")
211 expected = json.loads(open(reffile).read())
212
213 test_func = functools.partial(
214 topotest.router_json_cmp, router, "show ip pim neighbor json", expected
215 )
216 _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
217 assertmsg = "PIM router R1 did not converge"
218 assert res is None, assertmsg
219
220
221 def check_mcast_entry(entry, mcastaddr, pimrp):
222 "Helper function to check RP"
223 tgen = get_topogen()
224
225 logger.info(
226 "Testing PIM RP selection for ACL {} entry using {}".format(entry, mcastaddr)
227 )
228
229 with McastTesterHelper(tgen) as helper:
230 helper.run("h2", ["--send=0.7", mcastaddr, "h2-eth0"])
231 helper.run("h1", [mcastaddr, "h1-eth0"])
232
233 logger.info("mcast join and source for {} started".format(mcastaddr))
234
235 # tgen.mininet_cli()
236
237 router = tgen.gears["r1"]
238 reffile = os.path.join(CWD, "r1/acl_{}_pim_join.json".format(entry))
239 expected = json.loads(open(reffile).read())
240
241 logger.info("verifying pim join on r1 for {}".format(mcastaddr))
242 test_func = functools.partial(
243 topotest.router_json_cmp, router, "show ip pim join json", expected
244 )
245 _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
246 assertmsg = "PIM router r1 did not show join status"
247 assert res is None, assertmsg
248
249 logger.info("verifying pim join on PIM RP {} for {}".format(pimrp, mcastaddr))
250 router = tgen.gears[pimrp]
251 reffile = os.path.join(CWD, "{}/acl_{}_pim_join.json".format(pimrp, entry))
252 expected = json.loads(open(reffile).read())
253
254 test_func = functools.partial(
255 topotest.router_json_cmp, router, "show ip pim join json", expected
256 )
257 _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
258 assertmsg = "PIM router {} did not get selected as the PIM RP".format(pimrp)
259 assert res is None, assertmsg
260
261 return
262
263
264 def test_mcast_acl_1():
265 "Test 1st ACL entry 239.100.0.0/28 with 239.100.0.1"
266 tgen = get_topogen()
267
268 # Skip if previous fatal error condition is raised
269 if tgen.routers_have_failure():
270 pytest.skip(tgen.errors)
271
272 check_mcast_entry(1, "239.100.0.1", "r11")
273
274
275 def test_mcast_acl_2():
276 "Test 2nd ACL entry 239.100.0.17/32 with 239.100.0.17"
277 tgen = get_topogen()
278
279 # Skip if previous fatal error condition is raised
280 if tgen.routers_have_failure():
281 pytest.skip(tgen.errors)
282
283 check_mcast_entry(2, "239.100.0.17", "r12")
284
285
286 def test_mcast_acl_3():
287 "Test 3rd ACL entry 239.100.0.32/27 with 239.100.0.32"
288 tgen = get_topogen()
289
290 # Skip if previous fatal error condition is raised
291 if tgen.routers_have_failure():
292 pytest.skip(tgen.errors)
293
294 check_mcast_entry(3, "239.100.0.32", "r13")
295
296
297 def test_mcast_acl_4():
298 "Test 4th ACL entry 239.100.0.128/25 with 239.100.0.255"
299 tgen = get_topogen()
300
301 # Skip if previous fatal error condition is raised
302 if tgen.routers_have_failure():
303 pytest.skip(tgen.errors)
304
305 check_mcast_entry(4, "239.100.0.255", "r14")
306
307
308 def test_mcast_acl_5():
309 "Test 5th ACL entry 239.100.0.96/28 with 239.100.0.97"
310 tgen = get_topogen()
311
312 # Skip if previous fatal error condition is raised
313 if tgen.routers_have_failure():
314 pytest.skip(tgen.errors)
315
316 check_mcast_entry(5, "239.100.0.97", "r14")
317
318
319 def test_mcast_acl_6():
320 "Test 6th ACL entry 239.100.0.64/28 with 239.100.0.70"
321 tgen = get_topogen()
322
323 # Skip if previous fatal error condition is raised
324 if tgen.routers_have_failure():
325 pytest.skip(tgen.errors)
326
327 check_mcast_entry(6, "239.100.0.70", "r15")
328
329
330 if __name__ == "__main__":
331 args = ["-s"] + sys.argv[1:]
332 sys.exit(pytest.main(args))