]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[mirror_frr.git] / tests / topotests / isis_lfa_topo1 / test_isis_lfa_topo1.py
1 #!/usr/bin/env python
2
3 #
4 # test_isis_tilfa_topo1.py
5 # Part of NetDEF Topology Tests
6 #
7 # Copyright (c) 2020 by
8 # Network Device Education Foundation, Inc. ("NetDEF")
9 #
10 # Permission to use, copy, modify, and/or distribute this software
11 # for any purpose with or without fee is hereby granted, provided
12 # that the above copyright notice and this permission notice appear
13 # in all copies.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
16 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
18 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
19 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
21 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22 # OF THIS SOFTWARE.
23 #
24
25 """
26 test_isis_lfa_topo1.py:
27
28 +---------+
29 | |
30 +--------------------------------+ RT1 +-------------------------------+
31 | +-------------+ +-------------+ |
32 | | | | | |
33 | | +----+----+ | |
34 | | | |20 |
35 | | | | |
36 | | | | |
37 +----+----+ +----+----+ +----+----+ +----+----+ +----+----+
38 | | | | | | | | | |
39 | RT2 | 5 | RT3 | | RT4 | | RT5 | | RT6 |
40 | +--------+ | | | | | | |
41 | | | | | | | | | |
42 +----+----+ +----+----+ +----+----+ +----+----+ +----+----+
43 | | | | |
44 | | |15 | |
45 |5 | | | |
46 | | +----+----+ | |
47 | | | | | |
48 | +-------------+ RT7 +-------------+ |
49 +--------------------------------+ +-------------------------------+
50 | |
51 +---------+
52 """
53
54 import os
55 import sys
56 import pytest
57 import json
58 import time
59 import tempfile
60 from functools import partial
61
62 # Save the Current Working Directory to find configuration files.
63 CWD = os.path.dirname(os.path.realpath(__file__))
64 sys.path.append(os.path.join(CWD, "../"))
65
66 # pylint: disable=C0413
67 # Import topogen and topotest helpers
68 from lib import topotest
69 from lib.topogen import Topogen, TopoRouter, get_topogen
70 from lib.topolog import logger
71
72 # Required to instantiate the topology builder class.
73
74 pytestmark = [pytest.mark.isisd]
75
76 # Global multi-dimensional dictionary containing all expected outputs
77 outputs = {}
78
79
80 def build_topo(tgen):
81 "Build function"
82
83 #
84 # Define FRR Routers
85 #
86 for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
87 tgen.add_router(router)
88
89 #
90 # Define connections
91 #
92 switch = tgen.add_switch("s1")
93 switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
94 switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
95 switch = tgen.add_switch("s2")
96 switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3")
97 switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2")
98 switch = tgen.add_switch("s3")
99 switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3")
100 switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1")
101 switch = tgen.add_switch("s4")
102 switch.add_link(tgen.gears["rt1"], nodeif="eth-rt4")
103 switch.add_link(tgen.gears["rt4"], nodeif="eth-rt1")
104 switch = tgen.add_switch("s5")
105 switch.add_link(tgen.gears["rt1"], nodeif="eth-rt5")
106 switch.add_link(tgen.gears["rt5"], nodeif="eth-rt1")
107 switch = tgen.add_switch("s6")
108 switch.add_link(tgen.gears["rt1"], nodeif="eth-rt6")
109 switch.add_link(tgen.gears["rt6"], nodeif="eth-rt1")
110 switch = tgen.add_switch("s7")
111 switch.add_link(tgen.gears["rt2"], nodeif="eth-rt7")
112 switch.add_link(tgen.gears["rt7"], nodeif="eth-rt2")
113 switch = tgen.add_switch("s8")
114 switch.add_link(tgen.gears["rt3"], nodeif="eth-rt7")
115 switch.add_link(tgen.gears["rt7"], nodeif="eth-rt3")
116 switch = tgen.add_switch("s9")
117 switch.add_link(tgen.gears["rt4"], nodeif="eth-rt7")
118 switch.add_link(tgen.gears["rt7"], nodeif="eth-rt4")
119 switch = tgen.add_switch("s10")
120 switch.add_link(tgen.gears["rt5"], nodeif="eth-rt7")
121 switch.add_link(tgen.gears["rt7"], nodeif="eth-rt5")
122 switch = tgen.add_switch("s11")
123 switch.add_link(tgen.gears["rt6"], nodeif="eth-rt7")
124 switch.add_link(tgen.gears["rt7"], nodeif="eth-rt6")
125
126 #
127 # Populate multi-dimensional dictionary containing all expected outputs
128 #
129 files = ["show_ipv6_route.ref", "show_yang_interface_isis_adjacencies.ref"]
130 for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
131 outputs[rname] = {}
132 for step in range(1, 16 + 1):
133 outputs[rname][step] = {}
134 for file in files:
135 if step == 1:
136 # Get snapshots relative to the expected initial network convergence
137 filename = "{}/{}/step{}/{}".format(CWD, rname, step, file)
138 outputs[rname][step][file] = open(filename).read()
139 else:
140 if rname != "rt1":
141 continue
142 if file == "show_yang_interface_isis_adjacencies.ref":
143 continue
144
145 # Get diff relative to the previous step
146 filename = "{}/{}/step{}/{}.diff".format(CWD, rname, step, file)
147
148 # Create temporary files in order to apply the diff
149 f_in = tempfile.NamedTemporaryFile(mode="w")
150 f_in.write(outputs[rname][step - 1][file])
151 f_in.flush()
152 f_out = tempfile.NamedTemporaryFile(mode="r")
153 os.system(
154 "patch -s -o %s %s %s" % (f_out.name, f_in.name, filename)
155 )
156
157 # Store the updated snapshot and remove the temporary files
158 outputs[rname][step][file] = open(f_out.name).read()
159 f_in.close()
160 f_out.close()
161
162
163 def setup_module(mod):
164 "Sets up the pytest environment"
165 tgen = Topogen(build_topo, mod.__name__)
166 tgen.start_topology()
167
168 router_list = tgen.routers()
169
170 # For all registered routers, load the zebra configuration file
171 for rname, router in router_list.items():
172 router.load_config(
173 TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
174 )
175 router.load_config(
176 TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
177 )
178 router.load_config(
179 TopoRouter.RD_BFD, os.path.join(CWD, "/dev/null".format(rname))
180 )
181
182 tgen.start_router()
183
184
185 def teardown_module(mod):
186 "Teardown the pytest environment"
187 tgen = get_topogen()
188
189 # This function tears down the whole topology.
190 tgen.stop_topology()
191
192
193 def router_compare_json_output(rname, command, reference, wait=0.5, count=120):
194 "Compare router JSON output"
195
196 logger.info('Comparing router "%s" "%s" output', rname, command)
197
198 tgen = get_topogen()
199 expected = json.loads(reference)
200
201 # Run test function until we get an result. Wait at most 60 seconds.
202 test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
203 _, diff = topotest.run_and_expect(test_func, None, count=count, wait=wait)
204 assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
205 assert diff is None, assertmsg
206
207
208 #
209 # Step 1
210 #
211 # Test initial network convergence
212 #
213 def test_isis_adjacencies_step1():
214 logger.info("Test (step 1): check IS-IS adjacencies")
215 tgen = get_topogen()
216
217 # Skip if previous fatal error condition is raised
218 if tgen.routers_have_failure():
219 pytest.skip(tgen.errors)
220
221 for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
222 router_compare_json_output(
223 rname,
224 "show yang operational-data /frr-interface:lib isisd",
225 outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"],
226 )
227
228
229 def test_rib_ipv6_step1():
230 logger.info("Test (step 1): verify IPv6 RIB")
231 tgen = get_topogen()
232
233 # Skip if previous fatal error condition is raised
234 if tgen.routers_have_failure():
235 pytest.skip(tgen.errors)
236
237 for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
238 router_compare_json_output(
239 rname, "show ipv6 route isis json", outputs[rname][1]["show_ipv6_route.ref"]
240 )
241
242
243 #
244 # Step 2
245 #
246 # Action(s):
247 # -Disable LFA protection on all interfaces
248 #
249 # Expected changes:
250 # -rt1 should uninstall all backup nexthops from all routes
251 #
252 def test_rib_ipv6_step2():
253 logger.info("Test (step 2): verify IPv6 RIB")
254 tgen = get_topogen()
255
256 # Skip if previous fatal error condition is raised
257 if tgen.routers_have_failure():
258 pytest.skip(tgen.errors)
259
260 logger.info("Disabling LFA protection on all rt1 interfaces")
261 tgen.net["rt1"].cmd(
262 'vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute lfa"'
263 )
264 tgen.net["rt1"].cmd(
265 'vtysh -c "conf t" -c "interface eth-rt3" -c "no isis fast-reroute lfa"'
266 )
267 tgen.net["rt1"].cmd(
268 'vtysh -c "conf t" -c "interface eth-rt4" -c "no isis fast-reroute lfa"'
269 )
270 tgen.net["rt1"].cmd(
271 'vtysh -c "conf t" -c "interface eth-rt5" -c "no isis fast-reroute lfa"'
272 )
273 tgen.net["rt1"].cmd(
274 'vtysh -c "conf t" -c "interface eth-rt6" -c "no isis fast-reroute lfa"'
275 )
276
277 for rname in ["rt1"]:
278 router_compare_json_output(
279 rname, "show ipv6 route isis json", outputs[rname][2]["show_ipv6_route.ref"]
280 )
281
282
283 #
284 # Step 3
285 #
286 # Action(s):
287 # -Re-enable LFA protection on all interfaces
288 #
289 # Expected changes:
290 # -Revert changes from the previous step
291 #
292 def test_rib_ipv6_step3():
293 logger.info("Test (step 3): verify IPv6 RIB")
294 tgen = get_topogen()
295
296 # Skip if previous fatal error condition is raised
297 if tgen.routers_have_failure():
298 pytest.skip(tgen.errors)
299
300 logger.info("Re-enabling LFA protection on all rt1 interfaces")
301 tgen.net["rt1"].cmd(
302 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute lfa"'
303 )
304 tgen.net["rt1"].cmd(
305 'vtysh -c "conf t" -c "interface eth-rt3" -c "isis fast-reroute lfa"'
306 )
307 tgen.net["rt1"].cmd(
308 'vtysh -c "conf t" -c "interface eth-rt4" -c "isis fast-reroute lfa"'
309 )
310 tgen.net["rt1"].cmd(
311 'vtysh -c "conf t" -c "interface eth-rt5" -c "isis fast-reroute lfa"'
312 )
313 tgen.net["rt1"].cmd(
314 'vtysh -c "conf t" -c "interface eth-rt6" -c "isis fast-reroute lfa"'
315 )
316
317 for rname in ["rt1"]:
318 router_compare_json_output(
319 rname, "show ipv6 route isis json", outputs[rname][3]["show_ipv6_route.ref"]
320 )
321
322
323 #
324 # Step 4
325 #
326 # Action(s):
327 # -Disable LFA load-sharing
328 #
329 # Expected changes:
330 # -rt1 should use at most one backup nexthop for each route
331 #
332 def test_rib_ipv6_step4():
333 logger.info("Test (step 4): verify IPv6 RIB")
334 tgen = get_topogen()
335
336 # Skip if previous fatal error condition is raised
337 if tgen.routers_have_failure():
338 pytest.skip(tgen.errors)
339
340 logger.info("Disabling LFA load-sharing on rt1")
341 tgen.net["rt1"].cmd(
342 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute load-sharing disable"'
343 )
344
345 for rname in ["rt1"]:
346 router_compare_json_output(
347 rname, "show ipv6 route isis json", outputs[rname][4]["show_ipv6_route.ref"]
348 )
349
350
351 #
352 # Step 5
353 #
354 # Action(s):
355 # -Re-enable LFA load-sharing
356 #
357 # Expected changes:
358 # -Revert changes from the previous step
359 #
360 def test_rib_ipv6_step5():
361 logger.info("Test (step 5): verify IPv6 RIB")
362 tgen = get_topogen()
363
364 # Skip if previous fatal error condition is raised
365 if tgen.routers_have_failure():
366 pytest.skip(tgen.errors)
367
368 logger.info("Re-enabling LFA load-sharing on rt1")
369 tgen.net["rt1"].cmd(
370 'vtysh -c "conf t" -c "router isis 1" -c "no fast-reroute load-sharing disable"'
371 )
372
373 for rname in ["rt1"]:
374 router_compare_json_output(
375 rname, "show ipv6 route isis json", outputs[rname][5]["show_ipv6_route.ref"]
376 )
377
378
379 #
380 # Step 6
381 #
382 # Action(s):
383 # -Limit backup computation to critical priority prefixes only
384 #
385 # Expected changes:
386 # -rt1 should uninstall all backup nexthops from all routes
387 #
388 def test_rib_ipv6_step6():
389 logger.info("Test (step 6): verify IPv6 RIB")
390 tgen = get_topogen()
391
392 # Skip if previous fatal error condition is raised
393 if tgen.routers_have_failure():
394 pytest.skip(tgen.errors)
395
396 logger.info("Limiting backup computation to critical priority prefixes only")
397 tgen.net["rt1"].cmd(
398 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute priority-limit critical"'
399 )
400
401 for rname in ["rt1"]:
402 router_compare_json_output(
403 rname, "show ipv6 route isis json", outputs[rname][6]["show_ipv6_route.ref"]
404 )
405
406
407 #
408 # Step 7
409 #
410 # Action(s):
411 # -Configure a prefix priority list to classify rt7's loopback as a
412 # critical-priority prefix
413 #
414 # Expected changes:
415 # -rt1 should install backup nexthops for rt7's loopback route.
416 #
417 def test_rib_ipv6_step7():
418 logger.info("Test (step 7): verify IPv6 RIB")
419 tgen = get_topogen()
420
421 # Skip if previous fatal error condition is raised
422 if tgen.routers_have_failure():
423 pytest.skip(tgen.errors)
424
425 logger.info("Configuring a prefix priority list")
426 tgen.net["rt1"].cmd(
427 'vtysh -c "conf t" -c "router isis 1" -c "spf prefix-priority critical CRITICAL_DESTINATIONS"'
428 )
429 tgen.net["rt1"].cmd(
430 'vtysh -c "conf t" -c "ipv6 access-list CRITICAL_DESTINATIONS seq 5 permit 2001:db8:1000::7/128"'
431 )
432
433 for rname in ["rt1"]:
434 router_compare_json_output(
435 rname, "show ipv6 route isis json", outputs[rname][7]["show_ipv6_route.ref"]
436 )
437
438
439 #
440 # Step 8
441 #
442 # Action(s):
443 # -Revert previous changes related to prefix priorities
444 #
445 # Expected changes:
446 # -Revert changes from the previous two steps
447 #
448 def test_rib_ipv6_step8():
449 logger.info("Test (step 8): verify IPv6 RIB")
450 tgen = get_topogen()
451
452 # Skip if previous fatal error condition is raised
453 if tgen.routers_have_failure():
454 pytest.skip(tgen.errors)
455
456 logger.info("Reverting previous changes related to prefix priorities")
457 tgen.net["rt1"].cmd(
458 'vtysh -c "conf t" -c "no ipv6 access-list CRITICAL_DESTINATIONS seq 5 permit 2001:db8:1000::7/128"'
459 )
460 tgen.net["rt1"].cmd(
461 'vtysh -c "conf t" -c "router isis 1" -c "no fast-reroute priority-limit critical"'
462 )
463 tgen.net["rt1"].cmd(
464 'vtysh -c "conf t" -c "router isis 1" -c "no spf prefix-priority critical CRITICAL_DESTINATIONS"'
465 )
466
467 for rname in ["rt1"]:
468 router_compare_json_output(
469 rname, "show ipv6 route isis json", outputs[rname][8]["show_ipv6_route.ref"]
470 )
471
472
473 #
474 # Step 9
475 #
476 # Action(s):
477 # -Exclude eth-rt6 from LFA computation for eth-rt2's failure
478 #
479 # Expected changes:
480 # -Uninstall the eth-rt2 protecting backup nexthops that go through eth-rt6
481 #
482 def test_rib_ipv6_step9():
483 logger.info("Test (step 9): verify IPv6 RIB")
484 tgen = get_topogen()
485
486 # Skip if previous fatal error condition is raised
487 if tgen.routers_have_failure():
488 pytest.skip(tgen.errors)
489
490 logger.info("Excluding eth-rt6 from LFA computation for eth-rt2's failure")
491 tgen.net["rt1"].cmd(
492 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute lfa exclude interface eth-rt6"'
493 )
494
495 for rname in ["rt1"]:
496 router_compare_json_output(
497 rname, "show ipv6 route isis json", outputs[rname][9]["show_ipv6_route.ref"]
498 )
499
500
501 #
502 # Step 10
503 #
504 # Action(s):
505 # -Remove exclusion of eth-rt6 from LFA computation for eth-rt2's failure
506 #
507 # Expected changes:
508 # -Revert changes from the previous step
509 #
510 def test_rib_ipv6_step10():
511 logger.info("Test (step 10): verify IPv6 RIB")
512 tgen = get_topogen()
513
514 # Skip if previous fatal error condition is raised
515 if tgen.routers_have_failure():
516 pytest.skip(tgen.errors)
517
518 logger.info(
519 "Removing exclusion of eth-rt6 from LFA computation for eth-rt2's failure"
520 )
521 tgen.net["rt1"].cmd(
522 'vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute lfa exclude interface eth-rt6"'
523 )
524
525 for rname in ["rt1"]:
526 router_compare_json_output(
527 rname,
528 "show ipv6 route isis json",
529 outputs[rname][10]["show_ipv6_route.ref"],
530 )
531
532
533 #
534 # Step 11
535 #
536 # Action(s):
537 # -Add LFA tiebreaker: prefer node protecting backup path
538 #
539 # Expected changes:
540 # -rt1 should prefer backup nexthops that provide node protection
541 #
542 def test_rib_ipv6_step11():
543 logger.info("Test (step 11): verify IPv6 RIB")
544 tgen = get_topogen()
545
546 # Skip if previous fatal error condition is raised
547 if tgen.routers_have_failure():
548 pytest.skip(tgen.errors)
549
550 logger.info("Adding LFA tiebreaker: prefer node protecting backup path")
551 tgen.net["rt1"].cmd(
552 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker node-protecting index 10"'
553 )
554
555 for rname in ["rt1"]:
556 router_compare_json_output(
557 rname,
558 "show ipv6 route isis json",
559 outputs[rname][11]["show_ipv6_route.ref"],
560 )
561
562
563 #
564 # Step 12
565 #
566 # Action(s):
567 # -Add LFA tiebreaker: prefer backup path via downstream node
568 #
569 # Expected changes:
570 # -rt1 should prefer backup nexthops that satisfy the downstream condition
571 #
572 def test_rib_ipv6_step12():
573 logger.info("Test (step 12): 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 logger.info("Adding LFA tiebreaker: prefer backup path via downstream node")
581 tgen.net["rt1"].cmd(
582 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker downstream index 20"'
583 )
584
585 for rname in ["rt1"]:
586 router_compare_json_output(
587 rname,
588 "show ipv6 route isis json",
589 outputs[rname][12]["show_ipv6_route.ref"],
590 )
591
592
593 #
594 # Step 13
595 #
596 # Action(s):
597 # -Add LFA tiebreaker: prefer backup path with lowest total metric
598 #
599 # Expected changes:
600 # -rt1 should prefer backup nexthops that have the best metric
601 #
602 def test_rib_ipv6_step13():
603 logger.info("Test (step 13): verify IPv6 RIB")
604 tgen = get_topogen()
605
606 # Skip if previous fatal error condition is raised
607 if tgen.routers_have_failure():
608 pytest.skip(tgen.errors)
609
610 logger.info("Adding LFA tiebreaker: prefer backup path with lowest total metric")
611 tgen.net["rt1"].cmd(
612 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker lowest-backup-metric index 30"'
613 )
614
615 for rname in ["rt1"]:
616 router_compare_json_output(
617 rname,
618 "show ipv6 route isis json",
619 outputs[rname][13]["show_ipv6_route.ref"],
620 )
621
622
623 #
624 # Step 14
625 #
626 # Action(s):
627 # - Setting spf-delay-ietf init-delay of 15s
628 #
629 # Expected changes:
630 # - No routing table change
631 # - At the end of test, SPF reacts to a failure in 15s
632 #
633 def test_rib_ipv6_step14():
634 logger.info("Test (step 14): verify IPv6 RIB")
635 tgen = get_topogen()
636
637 # Skip if previous fatal error condition is raised
638 if tgen.routers_have_failure():
639 pytest.skip(tgen.errors)
640
641 logger.info("Setting spf-delay-ietf init-delay of 15s")
642 tgen.net["rt1"].cmd(
643 'vtysh -c "conf t" -c "router isis 1" -c "spf-delay-ietf init-delay 15000 short-delay 0 long-delay 0 holddown 0 time-to-learn 0"'
644 )
645
646 for rname in ["rt1"]:
647 router_compare_json_output(
648 rname,
649 "show ipv6 route isis json",
650 outputs[rname][14]["show_ipv6_route.ref"],
651 )
652
653
654 #
655 # Step 15
656 #
657 # Action(s):
658 # - shut the eth-rt2 interface on rt1
659 #
660 # Expected changes:
661 # - Route switchover of routes via eth-rt2
662 #
663 def test_rib_ipv6_step15():
664 logger.info("Test (step 15): verify IPv6 RIB")
665 tgen = get_topogen()
666
667 # Skip if previous fatal error condition is raised
668 if tgen.routers_have_failure():
669 pytest.skip(tgen.errors)
670
671 logger.info("Shut the interface to rt2 from the switch side and check fast-reroute")
672 tgen.net.cmd_raises("ip link set %s down" % tgen.net["s1"].intfs[0])
673
674 for rname in ["rt1"]:
675 router_compare_json_output(
676 rname,
677 "show ipv6 route isis json",
678 outputs[rname][15]["show_ipv6_route.ref"],
679 count=10,
680 wait=0.5,
681 )
682
683
684 #
685 # Step 16
686 #
687 # Action(s): wait for the convergence and SPF computation on rt1
688 #
689 # Expected changes:
690 # - convergence of IPv6 RIB
691 #
692 def test_rib_ipv6_step16():
693 logger.info("Test (step 16): verify IPv6 RIB")
694 tgen = get_topogen()
695
696 # Skip if previous fatal error condition is raised
697 if tgen.routers_have_failure():
698 pytest.skip(tgen.errors)
699
700 logger.info("Check SPF convergence")
701
702 for rname in ["rt1"]:
703 router_compare_json_output(
704 rname,
705 "show ipv6 route isis json",
706 outputs[rname][16]["show_ipv6_route.ref"],
707 )
708
709
710 #
711 # Step 17
712 #
713 # Action(s):
714 # - Unshut the interface to rt2 from the switch sid
715 #
716 # Expected changes:
717 # - The routing table converges
718 #
719 def test_rib_ipv6_step17():
720 logger.info("Test (step 17): verify IPv6 RIB")
721 tgen = get_topogen()
722
723 # Skip if previous fatal error condition is raised
724 if tgen.routers_have_failure():
725 pytest.skip(tgen.errors)
726
727 rname = "rt1"
728
729 logger.info("Unsetting spf-delay-ietf init-delay of 15s")
730 tgen.net[rname].cmd('vtysh -c "conf t" -c "router isis 1" -c "no spf-delay-ietf"')
731
732 logger.info(
733 "Unshut the interface to rt2 from the switch side and check fast-reroute"
734 )
735 tgen.net.cmd_raises("ip link set %s up" % tgen.net["s1"].intfs[0])
736
737 logger.info("Setting spf-delay-ietf init-delay of 15s")
738 tgen.net[rname].cmd(
739 'vtysh -c "conf t" -c "router isis 1" -c "spf-delay-ietf init-delay 15000 short-delay 0 long-delay 0 holddown 0 time-to-learn 0"'
740 )
741
742 router_compare_json_output(
743 rname,
744 "show ipv6 route isis json",
745 outputs[rname][14]["show_ipv6_route.ref"],
746 )
747
748
749 #
750 # Step 18
751 #
752 # Action(s):
753 # - drop traffic between rt1 and rt2 by shutting down the bridge between
754 # the routers. Interfaces on rt1 and rt2 stay up.
755 #
756 #
757 # Expected changes:
758 # - Route switchover of routes via eth-rt2
759 #
760 def test_rib_ipv6_step18():
761 def _rt2_neigh_down(router):
762 output = json.loads(router.vtysh_cmd("show isis neighbor rt2 json"))
763
764 """
765 Previous output was:
766 {
767 "areas":[
768 {
769 "area":"1",
770 "circuits":[
771 {
772 "circuit":0,
773 "adj":"rt2",
774 "interface":{
775 "name":"eth-rt2",
776 "state":"Up",
777 "adj-flaps":1,
778 "last-ago":"21s",
779 "circuit-type":"L1",
780 "speaks":"IPv6",
781 "topologies":{
782 "topo-0":"ipv6-unicast"
783 },
784 "snpa":"2020.2020.2020",
785 "area-address":{
786 "isonet":"49.0000"
787 },
788 "ipv6-link-local":{
789 "ipv6":"fe80::ac19:a8ff:fee5:f48f"
790 },
791 "adj-sid":{
792 }
793 },
794 "level":1,
795 "expires-in":"2s"
796 },
797 {
798 "circuit":0
799 },
800 {
801 "circuit":0
802 },
803 {
804 "circuit":0
805 },
806 {
807 "circuit":0
808 },
809 {
810 "circuit":0
811 }
812 ]
813 }
814 ]
815 """
816
817 expected = {
818 "areas": [
819 {
820 "area": "1",
821 "circuits": [
822 {"circuit": 0},
823 {"circuit": 0},
824 {"circuit": 0},
825 {"circuit": 0},
826 {"circuit": 0},
827 {"circuit": 0},
828 ],
829 }
830 ]
831 }
832
833 return topotest.json_cmp(output, expected, exact=True)
834
835 logger.info("Test (step 18): verify IPv6 RIB")
836 tgen = get_topogen()
837
838 # Skip if previous fatal error condition is raised
839 if tgen.routers_have_failure():
840 pytest.skip(tgen.errors)
841
842 logger.info("Drop traffic between rt1 and rt2")
843 tgen.net.cmd_raises("ip link set s1 down")
844
845 rname = "rt1"
846 router = tgen.gears[rname]
847 test_func = partial(_rt2_neigh_down, router)
848 success, result = topotest.run_and_expect(test_func, None, count=200, wait=0.05)
849 assert result is None, 'rt2 neighbor is still present on "{}"'.format(router)
850
851 router_compare_json_output(
852 rname,
853 "show ipv6 route isis json",
854 outputs[rname][15]["show_ipv6_route.ref"],
855 count=10,
856 wait=0.5,
857 )
858
859
860 #
861 # Step 19
862 #
863 # Action(s): wait for the convergence and SPF computation on rt1
864 #
865 # Expected changes:
866 # - convergence of IPv6 RIB
867 #
868 def test_rib_ipv6_step19():
869 logger.info("Test (step 19): verify IPv6 RIB")
870 tgen = get_topogen()
871
872 # Skip if previous fatal error condition is raised
873 if tgen.routers_have_failure():
874 pytest.skip(tgen.errors)
875
876 logger.info("Check SPF convergence")
877
878 for rname in ["rt1"]:
879 router_compare_json_output(
880 rname,
881 "show ipv6 route isis json",
882 outputs[rname][16]["show_ipv6_route.ref"],
883 )
884
885
886 #
887 # Step 20
888 #
889 # Action(s):
890 # - Unshut the switch from rt1 to rt2
891 #
892 # Expected changes:
893 # - The routing table goes back to the nominal state
894 #
895 def test_rib_ipv6_step20():
896 logger.info("Test (step 20): verify IPv6 RIB")
897 tgen = get_topogen()
898
899 # Skip if previous fatal error condition is raised
900 if tgen.routers_have_failure():
901 pytest.skip(tgen.errors)
902
903 rname = "rt1"
904
905 logger.info("Unsetting spf-delay-ietf init-delay of 15s")
906 tgen.net[rname].cmd('vtysh -c "conf t" -c "router isis 1" -c "no spf-delay-ietf"')
907
908 logger.info(
909 "Unshut the interface to rt2 from the switch side and check fast-reroute"
910 )
911 tgen.net.cmd_raises("ip link set s1 up")
912
913 logger.info("Setting spf-delay-ietf init-delay of 15s")
914 tgen.net[rname].cmd(
915 'vtysh -c "conf t" -c "router isis 1" -c "spf-delay-ietf init-delay 15000 short-delay 0 long-delay 0 holddown 0 time-to-learn 0"'
916 )
917
918 router_compare_json_output(
919 rname,
920 "show ipv6 route isis json",
921 outputs[rname][14]["show_ipv6_route.ref"],
922 )
923
924
925 #
926 # Step 21
927 #
928 # Action(s):
929 # - clear the rt2 ISIS neighbor on rt1
930 #
931 # Expected changes:
932 # - Route switchover of routes via eth-rt2
933 #
934 def test_rib_ipv6_step21():
935 logger.info("Test (step 21): verify IPv6 RIB")
936 tgen = get_topogen()
937
938 # Skip if previous fatal error condition is raised
939 if tgen.routers_have_failure():
940 pytest.skip(tgen.errors)
941
942 rname = "rt1"
943
944 logger.info("Clear the rt2 ISIS neighbor on rt1 and check fast-reroute")
945 tgen.gears[rname].vtysh_cmd("clear isis neighbor rt2")
946
947 router_compare_json_output(
948 rname,
949 "show ipv6 route isis json",
950 outputs[rname][15]["show_ipv6_route.ref"],
951 count=10,
952 wait=0.5,
953 )
954
955
956 #
957 # Step 22
958 #
959 # Action(s): wait for the convergence and SPF computation on rt1
960 #
961 # Expected changes:
962 # - convergence of IPv6 RIB
963 #
964 def test_rib_ipv6_step22():
965 logger.info("Test (step 22): verify IPv6 RIB")
966 tgen = get_topogen()
967
968 # Skip if previous fatal error condition is raised
969 if tgen.routers_have_failure():
970 pytest.skip(tgen.errors)
971
972 logger.info("Check SPF convergence")
973
974 for rname in ["rt1"]:
975 router_compare_json_output(
976 rname,
977 "show ipv6 route isis json",
978 outputs[rname][16]["show_ipv6_route.ref"],
979 )
980
981
982 #
983 # Step 23
984 #
985 # Action(s):
986 # - Setting BFD
987 #
988 # Expected changes:
989 # - No routing table change
990 # - BFD comes up
991 #
992 def test_rib_ipv6_step23():
993 logger.info("Test (step 23): verify IPv6 RIB")
994 tgen = get_topogen()
995
996 # Skip if previous fatal error condition is raised
997 if tgen.routers_have_failure():
998 pytest.skip(tgen.errors)
999
1000 logger.info("Setup BFD on rt1 and rt2")
1001 for rname in ["rt1", "rt2"]:
1002 conf_file = os.path.join(CWD, "{}/bfdd.conf".format(rname))
1003 tgen.net[rname].cmd("vtysh -f {}".format(conf_file))
1004
1005 logger.info("Set ISIS BFD")
1006 tgen.net["rt1"].cmd('vtysh -c "conf t" -c "int eth-rt2" -c "isis bfd"')
1007 tgen.net["rt2"].cmd('vtysh -c "conf t" -c "int eth-rt1" -c "isis bfd"')
1008
1009 rname = "rt1"
1010 expect = '[{"multihop":false,"interface":"eth-rt2","status":"up"}]'
1011 router_compare_json_output(rname, "show bfd peers json", expect)
1012
1013 router_compare_json_output(
1014 rname,
1015 "show ipv6 route isis json",
1016 outputs[rname][14]["show_ipv6_route.ref"],
1017 )
1018
1019
1020 #
1021 # Step 24
1022 #
1023 # Action(s):
1024 # - drop traffic between rt1 and rt2 by shutting down the bridge between
1025 # the routers. Interfaces on rt1 and rt2 stay up.
1026 #
1027 # Expected changes:
1028 # - BFD comes down before IS-IS
1029 # - Route switchover of routes via eth-rt2
1030 #
1031 def test_rib_ipv6_step24():
1032 def _bfd_down(router):
1033 output = json.loads(router.vtysh_cmd("show bfd peers json"))
1034 expected = []
1035 return topotest.json_cmp(output, expected, exact=True)
1036
1037 logger.info("Test (step 24): verify IPv6 RIB")
1038 tgen = get_topogen()
1039
1040 # Skip if previous fatal error condition is raised
1041 if tgen.routers_have_failure():
1042 pytest.skip(tgen.errors)
1043
1044 logger.info("Shut the interface to rt2 from the switch side and check fast-reroute")
1045 tgen.net.cmd_raises("ip link set s1 down")
1046
1047 rname = "rt1"
1048 router = tgen.gears[rname]
1049 test_func = partial(_bfd_down, router)
1050 success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.3)
1051 assert result is None, 'BFD session is still up on "{}"'.format(router)
1052
1053 router_compare_json_output(
1054 rname,
1055 "show ipv6 route isis json",
1056 outputs[rname][15]["show_ipv6_route.ref"],
1057 count=10,
1058 )
1059
1060
1061 #
1062 # Step 25
1063 #
1064 # Action(s): wait for the convergence and SPF computation on rt1
1065 #
1066 # Expected changes:
1067 # - convergence of IPv6 RIB
1068 #
1069 def test_rib_ipv6_step25():
1070 logger.info("Test (step 25): verify IPv6 RIB")
1071 tgen = get_topogen()
1072
1073 # Skip if previous fatal error condition is raised
1074 if tgen.routers_have_failure():
1075 pytest.skip(tgen.errors)
1076
1077 logger.info("Check SPF convergence")
1078
1079 for rname in ["rt1"]:
1080 router_compare_json_output(
1081 rname,
1082 "show ipv6 route isis json",
1083 outputs[rname][16]["show_ipv6_route.ref"],
1084 )
1085
1086
1087 # Memory leak test template
1088 def test_memory_leak():
1089 "Run the memory leak test and report results."
1090 tgen = get_topogen()
1091 if not tgen.is_memleak_enabled():
1092 pytest.skip("Memory leak test/report is disabled")
1093
1094 tgen.report_memory_leaks()
1095
1096
1097 if __name__ == "__main__":
1098 args = ["-s"] + sys.argv[1:]
1099 sys.exit(pytest.main(args))