]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/msdp_topo1/test_msdp_topo1.py
Merge pull request #13060 from opensourcerouting/feature/allow_peering_with_127.0.0.1
[mirror_frr.git] / tests / topotests / msdp_topo1 / test_msdp_topo1.py
1 #!/usr/bin/env python
2 # SPDX-License-Identifier: ISC
3
4 #
5 # test_msdp_topo1.py
6 # Part of NetDEF Topology Tests
7 #
8 # Copyright (c) 2021 by
9 # Network Device Education Foundation, Inc. ("NetDEF")
10 #
11
12 """
13 test_msdp_topo1.py: Test the FRR PIM MSDP peer.
14 """
15
16 import os
17 import sys
18 import json
19 from functools import partial
20 import pytest
21
22 # Save the Current Working Directory to find configuration files.
23 CWD = os.path.dirname(os.path.realpath(__file__))
24 sys.path.append(os.path.join(CWD, "../"))
25
26 # pylint: disable=C0413
27 # Import topogen and topotest helpers
28 from lib import topotest
29
30 # Required to instantiate the topology builder class.
31 from lib.topogen import Topogen, TopoRouter, get_topogen
32 from lib.topolog import logger
33
34 from lib.pim import McastTesterHelper
35
36 pytestmark = [pytest.mark.bgpd, pytest.mark.pimd]
37
38 app_helper = McastTesterHelper()
39
40
41 def build_topo(tgen):
42 "Build function"
43
44 # Create 4 routers
45 for routern in range(1, 5):
46 tgen.add_router("r{}".format(routern))
47
48 switch = tgen.add_switch("s1")
49 switch.add_link(tgen.gears["r1"])
50 switch.add_link(tgen.gears["r2"])
51
52 switch = tgen.add_switch("s2")
53 switch.add_link(tgen.gears["r1"])
54 switch.add_link(tgen.gears["r3"])
55
56 switch = tgen.add_switch("s3")
57 switch.add_link(tgen.gears["r2"])
58 switch.add_link(tgen.gears["r4"])
59
60 switch = tgen.add_switch("s4")
61 # switch.add_link(tgen.gears["r3"])
62 switch.add_link(tgen.gears["r4"])
63
64 switch = tgen.add_switch("s5")
65 switch.add_link(tgen.gears["r4"])
66
67 # Create a host connected and direct at r4:
68 tgen.add_host("h1", "192.168.4.100/24", "via 192.168.4.1")
69 switch.add_link(tgen.gears["h1"])
70
71 # Create a host connected and direct at r1:
72 switch = tgen.add_switch("s6")
73 tgen.add_host("h2", "192.168.10.100/24", "via 192.168.10.1")
74 switch.add_link(tgen.gears["r1"])
75 switch.add_link(tgen.gears["h2"])
76
77
78 def setup_module(mod):
79 "Sets up the pytest environment"
80 tgen = Topogen(build_topo, mod.__name__)
81 tgen.start_topology()
82
83 router_list = tgen.routers()
84 for rname, router in router_list.items():
85
86 daemon_file = "{}/{}/zebra.conf".format(CWD, rname)
87 if os.path.isfile(daemon_file):
88 router.load_config(TopoRouter.RD_ZEBRA, daemon_file)
89
90 daemon_file = "{}/{}/bgpd.conf".format(CWD, rname)
91 if os.path.isfile(daemon_file):
92 router.load_config(TopoRouter.RD_BGP, daemon_file)
93
94 daemon_file = "{}/{}/pimd.conf".format(CWD, rname)
95 if os.path.isfile(daemon_file):
96 router.load_config(TopoRouter.RD_PIM, daemon_file)
97
98 # Initialize all routers.
99 tgen.start_router()
100
101 app_helper.init(tgen)
102
103
104 def teardown_module(mod):
105 "Teardown the pytest environment"
106 tgen = get_topogen()
107 app_helper.cleanup()
108 tgen.stop_topology()
109
110
111 def test_bgp_convergence():
112 "Wait for BGP protocol convergence"
113 tgen = get_topogen()
114 if tgen.routers_have_failure():
115 pytest.skip(tgen.errors)
116
117 logger.info("waiting for protocols to converge")
118
119 def expect_loopback_route(router, iptype, route, proto):
120 "Wait until route is present on RIB for protocol."
121 logger.info("waiting route {} in {}".format(route, router))
122 test_func = partial(
123 topotest.router_json_cmp,
124 tgen.gears[router],
125 "show {} route json".format(iptype),
126 {route: [{"protocol": proto}]},
127 )
128 _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
129 assertmsg = '"{}" convergence failure'.format(router)
130 assert result is None, assertmsg
131
132 # Wait for R1
133 expect_loopback_route("r1", "ip", "10.254.254.2/32", "bgp")
134 expect_loopback_route("r1", "ip", "10.254.254.3/32", "bgp")
135 expect_loopback_route("r1", "ip", "10.254.254.4/32", "bgp")
136
137 # Wait for R2
138 expect_loopback_route("r2", "ip", "10.254.254.1/32", "bgp")
139 expect_loopback_route("r2", "ip", "10.254.254.3/32", "bgp")
140 expect_loopback_route("r2", "ip", "10.254.254.4/32", "bgp")
141
142 # Wait for R3
143 expect_loopback_route("r3", "ip", "10.254.254.1/32", "bgp")
144 expect_loopback_route("r3", "ip", "10.254.254.2/32", "bgp")
145 expect_loopback_route("r3", "ip", "10.254.254.4/32", "bgp")
146
147 # Wait for R4
148 expect_loopback_route("r4", "ip", "10.254.254.1/32", "bgp")
149 expect_loopback_route("r4", "ip", "10.254.254.2/32", "bgp")
150 expect_loopback_route("r4", "ip", "10.254.254.3/32", "bgp")
151
152
153 def _test_mroute_install():
154 "Test that multicast routes propagated and installed"
155 tgen = get_topogen()
156 if tgen.routers_have_failure():
157 pytest.skip(tgen.errors)
158
159 #
160 # Test R1 mroute
161 #
162 expect_1 = {
163 "229.1.2.3": {
164 "192.168.10.100": {
165 "iif": "r1-eth2",
166 "flags": "SFT",
167 "oil": {
168 "r1-eth0": {"source": "192.168.10.100", "group": "229.1.2.3"},
169 "r1-eth1": None,
170 },
171 }
172 }
173 }
174 # Create a deep copy of `expect_1`.
175 expect_2 = json.loads(json.dumps(expect_1))
176 # The route will be either via R2 or R3.
177 expect_2["229.1.2.3"]["192.168.10.100"]["oil"]["r1-eth0"] = None
178 expect_2["229.1.2.3"]["192.168.10.100"]["oil"]["r1-eth1"] = {
179 "source": "192.168.10.100",
180 "group": "229.1.2.3",
181 }
182
183 def test_r1_mroute():
184 "Test r1 multicast routing table function"
185 out = tgen.gears["r1"].vtysh_cmd("show ip mroute json", isjson=True)
186 if topotest.json_cmp(out, expect_1) is None:
187 return None
188 return topotest.json_cmp(out, expect_2)
189
190 logger.info("Waiting for R1 multicast routes")
191 _, val = topotest.run_and_expect(test_r1_mroute, None, count=55, wait=2)
192 assert val is None, "multicast route convergence failure"
193
194 #
195 # Test routers 2 and 3.
196 #
197 # NOTE: only one of the paths will get the multicast route.
198 #
199 expect_r2 = {
200 "229.1.2.3": {
201 "192.168.10.100": {
202 "iif": "r2-eth0",
203 "flags": "S",
204 "oil": {
205 "r2-eth1": {
206 "source": "192.168.10.100",
207 "group": "229.1.2.3",
208 }
209 },
210 }
211 }
212 }
213 expect_r3 = {
214 "229.1.2.3": {
215 "192.168.10.100": {
216 "iif": "r3-eth0",
217 "flags": "S",
218 "oil": {
219 "r3-eth1": {
220 "source": "192.168.10.100",
221 "group": "229.1.2.3",
222 }
223 },
224 }
225 }
226 }
227
228 def test_r2_r3_mroute():
229 "Test r2/r3 multicast routing table function"
230 r2_out = tgen.gears["r2"].vtysh_cmd("show ip mroute json", isjson=True)
231 r3_out = tgen.gears["r3"].vtysh_cmd("show ip mroute json", isjson=True)
232
233 if topotest.json_cmp(r2_out, expect_r2) is not None:
234 return topotest.json_cmp(r3_out, expect_r3)
235
236 return topotest.json_cmp(r2_out, expect_r2)
237
238 logger.info("Waiting for R2 and R3 multicast routes")
239 _, val = topotest.run_and_expect(test_r2_r3_mroute, None, count=55, wait=2)
240 assert val is None, "multicast route convergence failure"
241
242 #
243 # Test router 4
244 #
245 expect_4 = {
246 "229.1.2.3": {
247 "*": {
248 "iif": "lo",
249 "flags": "SC",
250 "oil": {
251 "pimreg": {
252 "source": "*",
253 "group": "229.1.2.3",
254 "inboundInterface": "lo",
255 "outboundInterface": "pimreg",
256 },
257 "r4-eth2": {
258 "source": "*",
259 "group": "229.1.2.3",
260 "inboundInterface": "lo",
261 "outboundInterface": "r4-eth2",
262 },
263 },
264 },
265 "192.168.10.100": {
266 "iif": "r4-eth0",
267 "flags": "ST",
268 "oil": {
269 "r4-eth2": {
270 "source": "192.168.10.100",
271 "group": "229.1.2.3",
272 "inboundInterface": "r4-eth0",
273 "outboundInterface": "r4-eth2",
274 }
275 },
276 },
277 }
278 }
279
280 test_func = partial(
281 topotest.router_json_cmp,
282 tgen.gears["r4"],
283 "show ip mroute json",
284 expect_4,
285 )
286 logger.info("Waiting for R4 multicast routes")
287 _, val = topotest.run_and_expect(test_func, None, count=55, wait=2)
288 assert val is None, "multicast route convergence failure"
289
290
291 def test_mroute_install():
292 tgen = get_topogen()
293 # pytest.skip("FOO")
294 if tgen.routers_have_failure():
295 pytest.skip(tgen.errors)
296
297 logger.info("Starting helper1")
298 mcastaddr = "229.1.2.3"
299 app_helper.run("h1", [mcastaddr, "h1-eth0"])
300
301 logger.info("Starting helper2")
302 app_helper.run("h2", ["--send=0.7", mcastaddr, "h2-eth0"])
303
304 _test_mroute_install()
305
306
307 def test_msdp():
308 """
309 Test MSDP convergence.
310
311 MSDP non meshed groups must propagate the whole SA database (not just
312 their own) to all peers because not all peers talk with each other.
313
314 This setup leads to a potential loop that can be prevented by checking
315 the route's first AS in AS path: it must match the remote eBGP AS number.
316 """
317 tgen = get_topogen()
318 if tgen.routers_have_failure():
319 pytest.skip(tgen.errors)
320
321 r1_expect = {
322 "192.168.0.2": {
323 "peer": "192.168.0.2",
324 "local": "192.168.0.1",
325 "state": "established",
326 },
327 "192.168.1.2": {
328 "peer": "192.168.1.2",
329 "local": "192.168.1.1",
330 "state": "established",
331 },
332 }
333 r1_sa_expect = {
334 "229.1.2.3": {
335 "192.168.10.100": {
336 "source": "192.168.10.100",
337 "group": "229.1.2.3",
338 "rp": "-",
339 "local": "yes",
340 "sptSetup": "-",
341 }
342 }
343 }
344 r2_expect = {
345 "192.168.0.1": {
346 "peer": "192.168.0.1",
347 "local": "192.168.0.2",
348 "state": "established",
349 },
350 "192.168.2.2": {
351 "peer": "192.168.2.2",
352 "local": "192.168.2.1",
353 "state": "established",
354 },
355 }
356 # Only R2 or R3 will get this SA.
357 r2_r3_sa_expect = {
358 "229.1.2.3": {
359 "192.168.10.100": {
360 "source": "192.168.10.100",
361 "group": "229.1.2.3",
362 "rp": "192.168.1.1",
363 "local": "no",
364 "sptSetup": "no",
365 }
366 }
367 }
368 r3_expect = {
369 "192.168.1.1": {
370 "peer": "192.168.1.1",
371 "local": "192.168.1.2",
372 "state": "established",
373 },
374 # "192.169.3.2": {
375 # "peer": "192.168.3.2",
376 # "local": "192.168.3.1",
377 # "state": "established"
378 # }
379 }
380 r4_expect = {
381 "192.168.2.1": {
382 "peer": "192.168.2.1",
383 "local": "192.168.2.2",
384 "state": "established",
385 },
386 # "192.168.3.1": {
387 # "peer": "192.168.3.1",
388 # "local": "192.168.3.2",
389 # "state": "established"
390 # }
391 }
392 r4_sa_expect = {
393 "229.1.2.3": {
394 "192.168.10.100": {
395 "source": "192.168.10.100",
396 "group": "229.1.2.3",
397 "rp": "192.168.1.1",
398 "local": "no",
399 "sptSetup": "yes",
400 }
401 }
402 }
403
404 for router in [
405 ("r1", r1_expect, r1_sa_expect),
406 ("r2", r2_expect, r2_r3_sa_expect),
407 ("r3", r3_expect, r2_r3_sa_expect),
408 ("r4", r4_expect, r4_sa_expect),
409 ]:
410 test_func = partial(
411 topotest.router_json_cmp,
412 tgen.gears[router[0]],
413 "show ip msdp peer json",
414 router[1],
415 )
416 logger.info("Waiting for {} msdp peer data".format(router[0]))
417 _, val = topotest.run_and_expect(test_func, None, count=30, wait=1)
418 assert val is None, "multicast route convergence failure"
419
420 test_func = partial(
421 topotest.router_json_cmp,
422 tgen.gears[router[0]],
423 "show ip msdp sa json",
424 router[2],
425 )
426 logger.info("Waiting for {} msdp SA data".format(router[0]))
427 _, val = topotest.run_and_expect(test_func, None, count=30, wait=1)
428 assert val is None, "multicast route convergence failure"
429
430
431 def test_memory_leak():
432 "Run the memory leak test and report results."
433 tgen = get_topogen()
434 if not tgen.is_memleak_enabled():
435 pytest.skip("Memory leak test/report is disabled")
436
437 tgen.report_memory_leaks()
438
439
440 if __name__ == "__main__":
441 args = ["-s"] + sys.argv[1:]
442 sys.exit(pytest.main(args))