]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / tests / topotests / isis_rlfa_topo1 / test_isis_rlfa_topo1.py
1 #!/usr/bin/env python
2 # SPDX-License-Identifier: ISC
3
4 #
5 # test_isis_rlfa_topo1.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_isis_rlfa_topo1.py:
14
15 +---------+ +---------+
16 | | | |
17 | RT1 | | RT2 |
18 | +---------------------+ |
19 | | | |
20 +---+-----+ +------+--+
21 | |
22 | |
23 | |
24 +---+-----+ +------+--+
25 | | | |
26 | RT3 | | RT4 |
27 | | | |
28 | | | |
29 +---+-----+ +------+--+
30 | |
31 | |
32 | |
33 +---+-----+ +------+--+
34 | | | |
35 | RT5 | | RT6 |
36 | | | |
37 | | | |
38 +---+-----+ +------+--+
39 | |
40 | |
41 | |
42 +---+-----+ +------+--+
43 | | | |
44 | RT7 | | RT8 |
45 | +---------------------+ |
46 | | | |
47 +---------+ +---------+
48 """
49
50 import os
51 import sys
52 import pytest
53 import json
54 import tempfile
55 from functools import partial
56
57 # Save the Current Working Directory to find configuration files.
58 CWD = os.path.dirname(os.path.realpath(__file__))
59 sys.path.append(os.path.join(CWD, "../"))
60
61 # pylint: disable=C0413
62 # Import topogen and topotest helpers
63 from lib import topotest
64 from lib.topogen import Topogen, TopoRouter, get_topogen
65 from lib.topolog import logger
66
67 # Required to instantiate the topology builder class.
68
69 pytestmark = [pytest.mark.isisd, pytest.mark.ldpd]
70
71 # Global multi-dimensional dictionary containing all expected outputs
72 outputs = {}
73
74
75 def build_topo(tgen):
76 "Build function"
77
78 #
79 # Define FRR Routers
80 #
81 for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7", "rt8"]:
82 tgen.add_router(router)
83
84 #
85 # Define connections
86 #
87 switch = tgen.add_switch("s1")
88 switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
89 switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
90 switch = tgen.add_switch("s2")
91 switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3")
92 switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1")
93 switch = tgen.add_switch("s3")
94 switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4")
95 switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2")
96 switch = tgen.add_switch("s4")
97 switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5")
98 switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3")
99 switch = tgen.add_switch("s5")
100 switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
101 switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
102 switch = tgen.add_switch("s6")
103 switch.add_link(tgen.gears["rt5"], nodeif="eth-rt7")
104 switch.add_link(tgen.gears["rt7"], nodeif="eth-rt5")
105 switch = tgen.add_switch("s7")
106 switch.add_link(tgen.gears["rt6"], nodeif="eth-rt8")
107 switch.add_link(tgen.gears["rt8"], nodeif="eth-rt6")
108 switch = tgen.add_switch("s8")
109 switch.add_link(tgen.gears["rt7"], nodeif="eth-rt8")
110 switch.add_link(tgen.gears["rt8"], nodeif="eth-rt7")
111
112 #
113 # Populate multi-dimensional dictionary containing all expected outputs
114 #
115 files = [
116 "show_ip_route.ref",
117 "show_ipv6_route.ref",
118 "show_yang_interface_isis_adjacencies.ref",
119 ]
120 for rname in ["rt1"]:
121 outputs[rname] = {}
122 for step in range(1, 10 + 1):
123 outputs[rname][step] = {}
124 for file in files:
125 if step == 1:
126 # Get snapshots relative to the expected initial network convergence
127 filename = "{}/{}/step{}/{}".format(CWD, rname, step, file)
128 outputs[rname][step][file] = open(filename).read()
129 else:
130 if file == "show_yang_interface_isis_adjacencies.ref":
131 continue
132
133 # Get diff relative to the previous step
134 filename = "{}/{}/step{}/{}.diff".format(CWD, rname, step, file)
135
136 # Create temporary files in order to apply the diff
137 f_in = tempfile.NamedTemporaryFile(mode="w")
138 f_in.write(outputs[rname][step - 1][file])
139 f_in.flush()
140 f_out = tempfile.NamedTemporaryFile(mode="r")
141 os.system(
142 "patch -s -o %s %s %s" % (f_out.name, f_in.name, filename)
143 )
144
145 # Store the updated snapshot and remove the temporary files
146 outputs[rname][step][file] = open(f_out.name).read()
147 f_in.close()
148 f_out.close()
149
150
151 def setup_module(mod):
152 "Sets up the pytest environment"
153 tgen = Topogen(build_topo, mod.__name__)
154 tgen.start_topology()
155
156 router_list = tgen.routers()
157
158 # For all registered routers, load the zebra configuration file
159 for rname, router in router_list.items():
160 router.load_config(
161 TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
162 )
163 router.load_config(
164 TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
165 )
166 router.load_config(
167 TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
168 )
169
170 tgen.start_router()
171
172
173 def teardown_module(mod):
174 "Teardown the pytest environment"
175 tgen = get_topogen()
176
177 # This function tears down the whole topology.
178 tgen.stop_topology()
179
180
181 def router_compare_json_output(rname, command, reference):
182 "Compare router JSON output"
183
184 logger.info('Comparing router "%s" "%s" output', rname, command)
185
186 tgen = get_topogen()
187 expected = json.loads(reference)
188
189 # Run test function until we get an result. Wait at most 60 seconds.
190 test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
191 _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
192 assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
193 assert diff is None, assertmsg
194
195
196 #
197 # Step 1
198 #
199 # Test initial network convergence
200 #
201 def test_isis_adjacencies_step1():
202 logger.info("Test (step 1): check IS-IS adjacencies")
203 tgen = get_topogen()
204
205 # Skip if previous fatal error condition is raised
206 if tgen.routers_have_failure():
207 pytest.skip(tgen.errors)
208
209 for rname in ["rt1"]:
210 router_compare_json_output(
211 rname,
212 "show yang operational-data /frr-interface:lib isisd",
213 outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"],
214 )
215
216
217 def test_rib_ipv4_step1():
218 logger.info("Test (step 1): verify IPv4 RIB")
219 tgen = get_topogen()
220
221 # Skip if previous fatal error condition is raised
222 if tgen.routers_have_failure():
223 pytest.skip(tgen.errors)
224
225 for rname in ["rt1"]:
226 router_compare_json_output(
227 rname, "show ip route isis json", outputs[rname][1]["show_ip_route.ref"]
228 )
229
230
231 def test_rib_ipv6_step1():
232 logger.info("Test (step 1): verify IPv6 RIB")
233 tgen = get_topogen()
234
235 # Skip if previous fatal error condition is raised
236 if tgen.routers_have_failure():
237 pytest.skip(tgen.errors)
238
239 for rname in ["rt1"]:
240 router_compare_json_output(
241 rname, "show ipv6 route isis json", outputs[rname][1]["show_ipv6_route.ref"]
242 )
243
244
245 #
246 # Step 2
247 #
248 # Action(s):
249 # -Configure rt8 (rt1's PQ router) to not accept targeted hello messages
250 #
251 # Expected changes:
252 # -All rt1 backup routes should be uninstalled
253 #
254 def test_rib_ipv4_step2():
255 logger.info("Test (step 2): verify IPv4 RIB")
256 tgen = get_topogen()
257
258 # Skip if previous fatal error condition is raised
259 if tgen.routers_have_failure():
260 pytest.skip(tgen.errors)
261
262 logger.info("Configuring rt8 to not accept targeted hello messages")
263 tgen.net["rt8"].cmd(
264 'vtysh -c "conf t" -c "mpls ldp" -c "address-family ipv4" -c "no discovery targeted-hello accept"'
265 )
266
267 for rname in ["rt1"]:
268 router_compare_json_output(
269 rname, "show ip route isis json", outputs[rname][2]["show_ip_route.ref"]
270 )
271
272
273 def test_rib_ipv6_step2():
274 logger.info("Test (step 2): verify IPv6 RIB")
275 tgen = get_topogen()
276
277 # Skip if previous fatal error condition is raised
278 if tgen.routers_have_failure():
279 pytest.skip(tgen.errors)
280
281 for rname in ["rt1"]:
282 router_compare_json_output(
283 rname, "show ipv6 route isis json", outputs[rname][2]["show_ipv6_route.ref"]
284 )
285
286
287 #
288 # Step 3
289 #
290 # Action(s):
291 # -Configure rt8 (rt1's PQ router) to accept targeted hello messages
292 #
293 # Expected changes:
294 # -All rt1 previously uninstalled backup routes should be reinstalled
295 #
296 def test_rib_ipv4_step3():
297 logger.info("Test (step 3): verify IPv4 RIB")
298 tgen = get_topogen()
299
300 # Skip if previous fatal error condition is raised
301 if tgen.routers_have_failure():
302 pytest.skip(tgen.errors)
303
304 logger.info("Configuring rt8 to accept targeted hello messages")
305 tgen.net["rt8"].cmd(
306 'vtysh -c "conf t" -c "mpls ldp" -c "address-family ipv4" -c "discovery targeted-hello accept"'
307 )
308
309 for rname in ["rt1"]:
310 router_compare_json_output(
311 rname, "show ip route isis json", outputs[rname][3]["show_ip_route.ref"]
312 )
313
314
315 def test_rib_ipv6_step3():
316 logger.info("Test (step 3): verify IPv6 RIB")
317 tgen = get_topogen()
318
319 # Skip if previous fatal error condition is raised
320 if tgen.routers_have_failure():
321 pytest.skip(tgen.errors)
322
323 for rname in ["rt1"]:
324 router_compare_json_output(
325 rname, "show ipv6 route isis json", outputs[rname][3]["show_ipv6_route.ref"]
326 )
327
328
329 #
330 # Step 4
331 #
332 # Action(s):
333 # -Disable RLFA on rt1's eth-rt2 interface
334 #
335 # Expected changes:
336 # -All non-ECMP routes whose primary nexthop is eth-rt2 should lose their backup nexthops
337 #
338 def test_rib_ipv4_step4():
339 logger.info("Test (step 4): verify IPv4 RIB")
340 tgen = get_topogen()
341
342 # Skip if previous fatal error condition is raised
343 if tgen.routers_have_failure():
344 pytest.skip(tgen.errors)
345
346 logger.info("Disabling RLFA on rt1's eth-rt2 interface")
347 tgen.net["rt1"].cmd(
348 'vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute remote-lfa tunnel mpls-ldp"'
349 )
350
351 for rname in ["rt1"]:
352 router_compare_json_output(
353 rname, "show ip route isis json", outputs[rname][4]["show_ip_route.ref"]
354 )
355
356
357 def test_rib_ipv6_step4():
358 logger.info("Test (step 4): verify IPv6 RIB")
359 tgen = get_topogen()
360
361 # Skip if previous fatal error condition is raised
362 if tgen.routers_have_failure():
363 pytest.skip(tgen.errors)
364
365 for rname in ["rt1"]:
366 router_compare_json_output(
367 rname, "show ipv6 route isis json", outputs[rname][4]["show_ipv6_route.ref"]
368 )
369
370
371 #
372 # Step 5
373 #
374 # Action(s):
375 # -Disable RLFA on rt1's eth-rt3 interface
376 #
377 # Expected changes:
378 # -All non-ECMP routes whose primary nexthop is eth-rt3 should lose their backup nexthops
379 #
380 def test_rib_ipv4_step5():
381 logger.info("Test (step 5): verify IPv4 RIB")
382 tgen = get_topogen()
383
384 # Skip if previous fatal error condition is raised
385 if tgen.routers_have_failure():
386 pytest.skip(tgen.errors)
387
388 logger.info("Disabling RLFA on rt1's eth-rt3 interface")
389 tgen.net["rt1"].cmd(
390 'vtysh -c "conf t" -c "interface eth-rt3" -c "no isis fast-reroute remote-lfa tunnel mpls-ldp"'
391 )
392
393 for rname in ["rt1"]:
394 router_compare_json_output(
395 rname, "show ip route isis json", outputs[rname][5]["show_ip_route.ref"]
396 )
397
398
399 def test_rib_ipv6_step5():
400 logger.info("Test (step 5): verify IPv6 RIB")
401 tgen = get_topogen()
402
403 # Skip if previous fatal error condition is raised
404 if tgen.routers_have_failure():
405 pytest.skip(tgen.errors)
406
407 for rname in ["rt1"]:
408 router_compare_json_output(
409 rname, "show ipv6 route isis json", outputs[rname][5]["show_ipv6_route.ref"]
410 )
411
412
413 #
414 # Step 6
415 #
416 # Action(s):
417 # -Re-enable RLFA on rt1's eth-rt2 and eth-rt3 interfaces
418 #
419 # Expected changes:
420 # -Revert changes from the previous two steps (reinstall all backup routes)
421 #
422 def test_rib_ipv4_step6():
423 logger.info("Test (step 6): verify IPv4 RIB")
424 tgen = get_topogen()
425
426 # Skip if previous fatal error condition is raised
427 if tgen.routers_have_failure():
428 pytest.skip(tgen.errors)
429
430 logger.info("Re-enabling RLFA on rt1's eth-rt2 and eth-rt3 interfaces")
431 tgen.net["rt1"].cmd(
432 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa tunnel mpls-ldp"'
433 )
434 tgen.net["rt1"].cmd(
435 'vtysh -c "conf t" -c "interface eth-rt3" -c "isis fast-reroute remote-lfa tunnel mpls-ldp"'
436 )
437
438 for rname in ["rt1"]:
439 router_compare_json_output(
440 rname, "show ip route isis json", outputs[rname][6]["show_ip_route.ref"]
441 )
442
443
444 def test_rib_ipv6_step6():
445 logger.info("Test (step 6): verify IPv6 RIB")
446 tgen = get_topogen()
447
448 # Skip if previous fatal error condition is raised
449 if tgen.routers_have_failure():
450 pytest.skip(tgen.errors)
451
452 for rname in ["rt1"]:
453 router_compare_json_output(
454 rname, "show ipv6 route isis json", outputs[rname][6]["show_ipv6_route.ref"]
455 )
456
457
458 #
459 # Step 7
460 #
461 # Action(s):
462 # -Configure a PQ node prefix-list filter
463 #
464 # Expected changes:
465 # -All backup routes should be uninstalled
466 #
467 def test_rib_ipv4_step7():
468 logger.info("Test (step 7): verify IPv4 RIB")
469 tgen = get_topogen()
470
471 # Skip if previous fatal error condition is raised
472 if tgen.routers_have_failure():
473 pytest.skip(tgen.errors)
474
475 logger.info("Configuring a PQ node prefix-list filter")
476 tgen.net["rt1"].cmd(
477 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute remote-lfa prefix-list PLIST"'
478 )
479
480 for rname in ["rt1"]:
481 router_compare_json_output(
482 rname, "show ip route isis json", outputs[rname][7]["show_ip_route.ref"]
483 )
484
485
486 def test_rib_ipv6_step7():
487 logger.info("Test (step 7): verify IPv6 RIB")
488 tgen = get_topogen()
489
490 # Skip if previous fatal error condition is raised
491 if tgen.routers_have_failure():
492 pytest.skip(tgen.errors)
493
494 for rname in ["rt1"]:
495 router_compare_json_output(
496 rname, "show ipv6 route isis json", outputs[rname][7]["show_ipv6_route.ref"]
497 )
498
499
500 #
501 # Step 8
502 #
503 # Action(s):
504 # -Configure a prefix-list allowing rt8 as a PQ node
505 #
506 # Expected changes:
507 # -All backup routes should be installed again
508 #
509 def test_rib_ipv4_step8():
510 logger.info("Test (step 8): verify IPv4 RIB")
511 tgen = get_topogen()
512
513 # Skip if previous fatal error condition is raised
514 if tgen.routers_have_failure():
515 pytest.skip(tgen.errors)
516
517 logger.info("Configuring a prefix-list allowing rt8 as a PQ node")
518 tgen.net["rt1"].cmd(
519 'vtysh -c "conf t" -c "ip prefix-list PLIST seq 5 permit 10.0.255.8/32"'
520 )
521
522 for rname in ["rt1"]:
523 router_compare_json_output(
524 rname, "show ip route isis json", outputs[rname][8]["show_ip_route.ref"]
525 )
526
527
528 def test_rib_ipv6_step8():
529 logger.info("Test (step 8): verify IPv6 RIB")
530 tgen = get_topogen()
531
532 # Skip if previous fatal error condition is raised
533 if tgen.routers_have_failure():
534 pytest.skip(tgen.errors)
535
536 for rname in ["rt1"]:
537 router_compare_json_output(
538 rname, "show ipv6 route isis json", outputs[rname][8]["show_ipv6_route.ref"]
539 )
540
541
542 #
543 # Step 9
544 #
545 # Action(s):
546 # -Change the maximum metric up to the PQ node to 30 on the eth-rt2 interface
547 #
548 # Expected changes:
549 # -All non-ECMP routes whose primary nexthop is eth-rt2 should lose their backup nexthops
550 #
551 def test_rib_ipv4_step9():
552 logger.info("Test (step 9): verify IPv4 RIB")
553 tgen = get_topogen()
554
555 # Skip if previous fatal error condition is raised
556 if tgen.routers_have_failure():
557 pytest.skip(tgen.errors)
558
559 logger.info(
560 "Changing the maximum metric up to the PQ node to 30 on the eth-rt2 interface"
561 )
562 tgen.net["rt1"].cmd(
563 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa maximum-metric 30"'
564 )
565
566 for rname in ["rt1"]:
567 router_compare_json_output(
568 rname, "show ip route isis json", outputs[rname][9]["show_ip_route.ref"]
569 )
570
571
572 def test_rib_ipv6_step9():
573 logger.info("Test (step 9): verify IPv6 RIB")
574 tgen = get_topogen()
575
576 # Skip if previous fatal error condition is raised
577 if tgen.routers_have_failure():
578 pytest.skip(tgen.errors)
579
580 for rname in ["rt1"]:
581 router_compare_json_output(
582 rname, "show ipv6 route isis json", outputs[rname][9]["show_ipv6_route.ref"]
583 )
584
585
586 #
587 # Step 10
588 #
589 # Action(s):
590 # -Change the maximum metric up to the PQ node to 40 on the eth-rt2 interface
591 #
592 # Expected changes:
593 # -All non-ECMP routes whose primary nexthop is eth-rt2 should recover their backup nexthops
594 #
595 def test_rib_ipv4_step10():
596 logger.info("Test (step 10): verify IPv4 RIB")
597 tgen = get_topogen()
598
599 # Skip if previous fatal error condition is raised
600 if tgen.routers_have_failure():
601 pytest.skip(tgen.errors)
602
603 logger.info(
604 "Changing the maximum metric up to the PQ node to 40 on the eth-rt2 interface"
605 )
606 tgen.net["rt1"].cmd(
607 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa maximum-metric 40"'
608 )
609
610 for rname in ["rt1"]:
611 router_compare_json_output(
612 rname, "show ip route isis json", outputs[rname][10]["show_ip_route.ref"]
613 )
614
615
616 def test_rib_ipv6_step10():
617 logger.info("Test (step 10): verify IPv6 RIB")
618 tgen = get_topogen()
619
620 # Skip if previous fatal error condition is raised
621 if tgen.routers_have_failure():
622 pytest.skip(tgen.errors)
623
624 for rname in ["rt1"]:
625 router_compare_json_output(
626 rname,
627 "show ipv6 route isis json",
628 outputs[rname][10]["show_ipv6_route.ref"],
629 )
630
631
632 # Memory leak test template
633 def test_memory_leak():
634 "Run the memory leak test and report results."
635 tgen = get_topogen()
636 if not tgen.is_memleak_enabled():
637 pytest.skip("Memory leak test/report is disabled")
638
639 tgen.report_memory_leaks()
640
641
642 if __name__ == "__main__":
643 args = ["-s"] + sys.argv[1:]
644 sys.exit(pytest.main(args))