]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/isis_sr_te_topo1/test_isis_sr_te_topo1.py
Merge pull request #9019 from pjdruddy/ospfv3-early-break-list-walk
[mirror_frr.git] / tests / topotests / isis_sr_te_topo1 / test_isis_sr_te_topo1.py
1 #!/usr/bin/env python
2
3 #
4 # test_isis_sr_topo1.py
5 # Part of NetDEF Topology Tests
6 #
7 # Copyright (c) 2019 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_sr_te_topo1.py:
27
28 +---------+
29 | |
30 | RT1 |
31 | 1.1.1.1 |
32 | |
33 +---------+
34 |eth-sw1
35 |
36 |
37 |
38 +---------+ | +---------+
39 | | | | |
40 | RT2 |eth-sw1 | eth-sw1| RT3 |
41 | 2.2.2.2 +----------+----------+ 3.3.3.3 |
42 | | 10.0.1.0/24 | |
43 +---------+ +---------+
44 eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2
45 | | | |
46 10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24
47 | | | |
48 eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2
49 +---------+ +---------+
50 | | | |
51 | RT4 | 10.0.6.0/24 | RT5 |
52 | 4.4.4.4 +---------------------+ 5.5.5.5 |
53 | |eth-rt5 eth-rt4| |
54 +---------+ +---------+
55 eth-rt6| |eth-rt6
56 | |
57 10.0.7.0/24| |10.0.8.0/24
58 | +---------+ |
59 | | | |
60 | | RT6 | |
61 +----------+ 6.6.6.6 +-----------+
62 eth-rt4| |eth-rt5
63 +---------+
64 |eth-dst (.1)
65 |
66 |10.0.11.0/24
67 |
68 |eth-rt6 (.2)
69 +---------+
70 | |
71 | DST |
72 | 9.9.9.2 |
73 | |
74 +---------+
75
76 """
77
78 import os
79 import sys
80 import pytest
81 import json
82 from functools import partial
83
84 # Save the Current Working Directory to find configuration files.
85 CWD = os.path.dirname(os.path.realpath(__file__))
86 sys.path.append(os.path.join(CWD, "../"))
87
88 # pylint: disable=C0413
89 # Import topogen and topotest helpers
90 from lib import topotest
91 from lib.topogen import Topogen, TopoRouter, get_topogen
92 from lib.topolog import logger
93
94 # Required to instantiate the topology builder class.
95
96 pytestmark = [pytest.mark.bgpd, pytest.mark.isisd, pytest.mark.pathd]
97
98
99 def build_topo(tgen):
100 "Build function"
101
102 #
103 # Define FRR Routers
104 #
105 for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "dst"]:
106 tgen.add_router(router)
107
108 #
109 # Define connections
110 #
111 switch = tgen.add_switch("s1")
112 switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1")
113 switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1")
114 switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1")
115
116 switch = tgen.add_switch("s2")
117 switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-1")
118 switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-1")
119
120 switch = tgen.add_switch("s3")
121 switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-2")
122 switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-2")
123
124 switch = tgen.add_switch("s4")
125 switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-1")
126 switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-1")
127
128 switch = tgen.add_switch("s5")
129 switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-2")
130 switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-2")
131
132 switch = tgen.add_switch("s6")
133 switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
134 switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
135
136 switch = tgen.add_switch("s7")
137 switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
138 switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
139
140 switch = tgen.add_switch("s8")
141 switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6")
142 switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5")
143
144 switch = tgen.add_switch("s9")
145 switch.add_link(tgen.gears["rt6"], nodeif="eth-dst")
146 switch.add_link(tgen.gears["dst"], nodeif="eth-rt6")
147
148
149 def setup_module(mod):
150 "Sets up the pytest environment"
151
152 tgen = Topogen(build_topo, mod.__name__)
153
154 frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir")
155 if not os.path.isfile(os.path.join(frrdir, "pathd")):
156 pytest.skip("pathd daemon wasn't built")
157
158 tgen.start_topology()
159
160 router_list = tgen.routers()
161
162 # For all registered routers, load the zebra configuration file
163 for rname, router in router_list.items():
164 router.load_config(
165 TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
166 )
167 router.load_config(
168 TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
169 )
170 router.load_config(
171 TopoRouter.RD_PATH, os.path.join(CWD, "{}/pathd.conf".format(rname))
172 )
173 router.load_config(
174 TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
175 )
176
177 tgen.start_router()
178
179
180 def teardown_module(mod):
181 "Teardown the pytest environment"
182 tgen = get_topogen()
183
184 # This function tears down the whole topology.
185 tgen.stop_topology()
186
187
188 def setup_testcase(msg):
189 logger.info(msg)
190 tgen = get_topogen()
191
192 # Skip if previous fatal error condition is raised
193 if tgen.routers_have_failure():
194 pytest.skip(tgen.errors)
195
196 return tgen
197
198
199 def print_cmd_result(rname, command):
200 print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False))
201
202
203 def compare_json_test(router, command, reference, exact):
204 output = router.vtysh_cmd(command, isjson=True)
205 result = topotest.json_cmp(output, reference)
206
207 # Note: topotest.json_cmp() just checks on inclusion of keys.
208 # For exact matching also compare the other way around.
209 if not result and exact:
210 return topotest.json_cmp(reference, output)
211 else:
212 return result
213
214
215 def cmp_json_output(rname, command, reference, exact=False):
216 "Compare router JSON output"
217
218 logger.info('Comparing router "%s" "%s" output', rname, command)
219
220 tgen = get_topogen()
221 filename = "{}/{}/{}".format(CWD, rname, reference)
222 expected = json.loads(open(filename).read())
223
224 # Run test function until we get an result. Wait at most 60 seconds.
225 test_func = partial(compare_json_test, tgen.gears[rname], command, expected, exact)
226 _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
227 assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
228 assert diff is None, assertmsg
229
230
231 def cmp_json_output_exact(rname, command, reference):
232 return cmp_json_output(rname, command, reference, True)
233
234
235 def add_candidate_path(rname, endpoint, pref, name, segment_list="default"):
236 get_topogen().net[rname].cmd(
237 """ \
238 vtysh -c "conf t" \
239 -c "segment-routing" \
240 -c "traffic-eng" \
241 -c "policy color 1 endpoint """
242 + endpoint
243 + """" \
244 -c "candidate-path preference """
245 + str(pref)
246 + """ name """
247 + name
248 + """ explicit segment-list """
249 + segment_list
250 + '''"'''
251 )
252
253
254 def delete_candidate_path(rname, endpoint, pref):
255 get_topogen().net[rname].cmd(
256 """ \
257 vtysh -c "conf t" \
258 -c "segment-routing" \
259 -c "traffic-eng" \
260 -c "policy color 1 endpoint """
261 + endpoint
262 + """" \
263 -c "no candidate-path preference """
264 + str(pref)
265 + '''"'''
266 )
267
268
269 def add_segment(rname, name, index, label):
270 get_topogen().net[rname].cmd(
271 """ \
272 vtysh -c "conf t" \
273 -c "segment-routing" \
274 -c "traffic-eng" \
275 -c "segment-list """
276 + name
277 + """" \
278 -c "index """
279 + str(index)
280 + """ mpls label """
281 + str(label)
282 + '''"'''
283 )
284
285
286 def delete_segment(rname, name, index):
287 get_topogen().net[rname].cmd(
288 """ \
289 vtysh -c "conf t" \
290 -c "segment-routing" \
291 -c "traffic-eng" \
292 -c "segment-list """
293 + name
294 + """" \
295 -c "no index """
296 + str(index)
297 + '''"'''
298 )
299
300
301 def create_sr_policy(rname, endpoint, bsid):
302 get_topogen().net[rname].cmd(
303 """ \
304 vtysh -c "conf t" \
305 -c "segment-routing" \
306 -c "traffic-eng" \
307 -c "policy color 1 endpoint """
308 + endpoint
309 + """" \
310 -c "name default" \
311 -c "binding-sid """
312 + str(bsid)
313 + '''"'''
314 )
315
316
317 def delete_sr_policy(rname, endpoint):
318 get_topogen().net[rname].cmd(
319 """ \
320 vtysh -c "conf t" \
321 -c "segment-routing" \
322 -c "traffic-eng" \
323 -c "no policy color 1 endpoint """
324 + endpoint
325 + '''"'''
326 )
327
328
329 def create_prefix_sid(rname, prefix, sid):
330 get_topogen().net[rname].cmd(
331 """ \
332 vtysh -c "conf t" \
333 -c "router isis 1" \
334 -c "segment-routing prefix """
335 + prefix
336 + " index "
337 + str(sid)
338 + '''"'''
339 )
340
341
342 def delete_prefix_sid(rname, prefix):
343 get_topogen().net[rname].cmd(
344 ''' \
345 vtysh -c "conf t" \
346 -c "router isis 1" \
347 -c "no segment-routing prefix "'''
348 + prefix
349 )
350
351
352 #
353 # Step 1
354 #
355 # Checking the MPLS table using a single SR Policy and a single Candidate Path
356 #
357 def test_srte_init_step1():
358 setup_testcase("Test (step 1): wait for IS-IS convergence / label distribution")
359
360 for rname in ["rt1", "rt6"]:
361 cmp_json_output(
362 rname, "show mpls table json", "step1/show_mpls_table_without_candidate.ref"
363 )
364
365
366 def test_srte_add_candidate_check_mpls_table_step1():
367 setup_testcase("Test (step 1): check MPLS table regarding the added Candidate Path")
368
369 for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
370 add_candidate_path(rname, endpoint, 100, "default")
371 cmp_json_output(
372 rname, "show mpls table json", "step1/show_mpls_table_with_candidate.ref"
373 )
374 delete_candidate_path(rname, endpoint, 100)
375
376
377 def test_srte_reinstall_sr_policy_check_mpls_table_step1():
378 setup_testcase(
379 "Test (step 1): check MPLS table after the SR Policy was removed and reinstalled"
380 )
381
382 for rname, endpoint, bsid in [("rt1", "6.6.6.6", 1111), ("rt6", "1.1.1.1", 6666)]:
383 add_candidate_path(rname, endpoint, 100, "default")
384 delete_sr_policy(rname, endpoint)
385 cmp_json_output(
386 rname, "show mpls table json", "step1/show_mpls_table_without_candidate.ref"
387 )
388 create_sr_policy(rname, endpoint, bsid)
389 add_candidate_path(rname, endpoint, 100, "default")
390 cmp_json_output(
391 rname, "show mpls table json", "step1/show_mpls_table_with_candidate.ref"
392 )
393 delete_candidate_path(rname, endpoint, 100)
394
395
396 #
397 # Step 2
398 #
399 # Checking pathd operational data using a single SR Policy and a single Candidate Path
400 #
401 def test_srte_bare_policy_step2():
402 setup_testcase("Test (step 2): bare SR Policy should not be operational")
403
404 for rname in ["rt1", "rt6"]:
405 cmp_json_output_exact(
406 rname,
407 "show yang operational-data /frr-pathd:pathd pathd",
408 "step2/show_operational_data.ref",
409 )
410
411
412 def test_srte_add_candidate_check_operational_data_step2():
413 setup_testcase(
414 "Test (step 2): add single Candidate Path, SR Policy should be operational"
415 )
416
417 for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
418 add_candidate_path(rname, endpoint, 100, "default")
419 cmp_json_output(
420 rname,
421 "show yang operational-data /frr-pathd:pathd pathd",
422 "step2/show_operational_data_with_candidate.ref",
423 )
424
425
426 def test_srte_config_remove_candidate_check_operational_data_step2():
427 setup_testcase(
428 "Test (step 2): remove single Candidate Path, SR Policy should not be operational anymore"
429 )
430
431 for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
432 delete_candidate_path(rname, endpoint, 100)
433 cmp_json_output_exact(
434 rname,
435 "show yang operational-data /frr-pathd:pathd pathd",
436 "step2/show_operational_data.ref",
437 )
438
439
440 #
441 # Step 3
442 #
443 # Testing the Candidate Path selection
444 #
445 def test_srte_add_two_candidates_step3():
446 setup_testcase("Test (step 3): second Candidate Path has higher Priority")
447
448 for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
449 for pref, cand_name in [("100", "first"), ("200", "second")]:
450 add_candidate_path(rname, endpoint, pref, cand_name)
451 cmp_json_output(
452 rname,
453 "show yang operational-data /frr-pathd:pathd pathd",
454 "step3/show_operational_data_with_two_candidates.ref",
455 )
456
457 # cleanup
458 for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
459 for pref in ["100", "200"]:
460 delete_candidate_path(rname, endpoint, pref)
461
462
463 def test_srte_add_two_candidates_with_reverse_priority_step3():
464 setup_testcase("Test (step 3): second Candidate Path has lower Priority")
465
466 # Use reversed priorities here
467 for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
468 for pref, cand_name in [("200", "first"), ("100", "second")]:
469 add_candidate_path(rname, endpoint, pref, cand_name)
470 cmp_json_output(
471 rname,
472 "show yang operational-data /frr-pathd:pathd pathd",
473 "step3/show_operational_data_with_two_candidates.ref",
474 )
475
476 # cleanup
477 for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
478 for pref in ["100", "200"]:
479 delete_candidate_path(rname, endpoint, pref)
480
481
482 def test_srte_remove_best_candidate_step3():
483 setup_testcase("Test (step 3): delete the Candidate Path with higher priority")
484
485 for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
486 for pref, cand_name in [("100", "first"), ("200", "second")]:
487 add_candidate_path(rname, endpoint, pref, cand_name)
488
489 # Delete candidate with higher priority
490 for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
491 delete_candidate_path(rname, endpoint, 200)
492
493 # Candidate with lower priority should get active now
494 for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
495 cmp_json_output(
496 rname,
497 "show yang operational-data /frr-pathd:pathd pathd",
498 "step3/show_operational_data_with_single_candidate.ref",
499 )
500 # cleanup
501 delete_candidate_path(rname, endpoint, 100)
502
503
504 #
505 # Step 4
506 #
507 # Checking MPLS table with a single SR Policy and a Candidate Path with different Segment Lists and other modifications
508 #
509 def test_srte_change_segment_list_check_mpls_table_step4():
510 setup_testcase("Test (step 4): check MPLS table for changed Segment List")
511
512 for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
513 add_candidate_path(rname, endpoint, 100, "default")
514 # now change the segment list name
515 add_candidate_path(rname, endpoint, 100, "default", "test")
516 cmp_json_output(rname, "show mpls table json", "step4/show_mpls_table.ref")
517 delete_candidate_path(rname, endpoint, 100)
518
519
520 def test_srte_segment_list_add_segment_check_mpls_table_step4():
521 setup_testcase(
522 "Test (step 4): check MPLS table for added (then changed and finally deleted) segment"
523 )
524
525 add_candidate_path("rt1", "6.6.6.6", 100, "default", "test")
526
527 # first add a new segment
528 add_segment("rt1", "test", 25, 16050)
529 cmp_json_output(
530 "rt1", "show mpls table json", "step4/show_mpls_table_add_segment.ref"
531 )
532
533 # ... then change it ...
534 add_segment("rt1", "test", 25, 16030)
535 cmp_json_output(
536 "rt1", "show mpls table json", "step4/show_mpls_table_change_segment.ref"
537 )
538
539 # ... and finally delete it
540 delete_segment("rt1", "test", 25)
541 cmp_json_output("rt1", "show mpls table json", "step4/show_mpls_table.ref")
542 delete_candidate_path("rt1", "6.6.6.6", 100)
543
544
545 #
546 # Step 5
547 #
548 # Checking the nexthop using a single SR Policy and a Candidate Path with configured route-map
549 #
550 def test_srte_route_map_with_sr_policy_check_nextop_step5():
551 setup_testcase(
552 "Test (step 5): recursive nexthop learned through BGP neighbour should be aligned with SR Policy from route-map"
553 )
554
555 # (re-)build the SR Policy two times to ensure that reinstalling still works
556 for i in [1, 2]:
557 cmp_json_output(
558 "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_inactive_srte.ref"
559 )
560
561 delete_sr_policy("rt1", "6.6.6.6")
562 cmp_json_output(
563 "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_inactive_srte.ref"
564 )
565
566 create_sr_policy("rt1", "6.6.6.6", 1111)
567 cmp_json_output(
568 "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_inactive_srte.ref"
569 )
570
571 add_candidate_path("rt1", "6.6.6.6", 100, "default")
572 cmp_json_output(
573 "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
574 )
575
576 delete_candidate_path("rt1", "6.6.6.6", 100)
577
578
579 def test_srte_route_map_with_sr_policy_reinstall_prefix_sid_check_nextop_step5():
580 setup_testcase(
581 "Test (step 5): remove and re-install prefix SID on fist path element and check SR Policy activity"
582 )
583
584 # first add a candidate path so the SR Policy is active
585 add_candidate_path("rt1", "6.6.6.6", 100, "default")
586 cmp_json_output(
587 "rt1",
588 "show yang operational-data /frr-pathd:pathd pathd",
589 "step5/show_operational_data_active.ref",
590 )
591
592 # delete prefix SID from first element of the configured path and check
593 # if the SR Policy is inactive since the label can't be resolved anymore
594 delete_prefix_sid("rt5", "5.5.5.5/32")
595 cmp_json_output(
596 "rt1",
597 "show yang operational-data /frr-pathd:pathd pathd",
598 "step5/show_operational_data_inactive.ref",
599 )
600 cmp_json_output(
601 "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_inactive_srte.ref"
602 )
603
604 # re-create the prefix SID and check if the SR Policy is active
605 create_prefix_sid("rt5", "5.5.5.5/32", 50)
606 cmp_json_output(
607 "rt1",
608 "show yang operational-data /frr-pathd:pathd pathd",
609 "step5/show_operational_data_active.ref",
610 )
611 cmp_json_output(
612 "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
613 )
614
615
616 # Memory leak test template
617 def test_memory_leak():
618 "Run the memory leak test and report results."
619 tgen = get_topogen()
620 if not tgen.is_memleak_enabled():
621 pytest.skip("Memory leak test/report is disabled")
622
623 tgen.report_memory_leaks()
624
625
626 if __name__ == "__main__":
627 args = ["-s"] + sys.argv[1:]
628 sys.exit(pytest.main(args))