]>
git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/ospf_sr_te_topo1/test_ospf_sr_te_topo1.py
4 # test_ospf_sr_te_topo1.py
6 # Copyright (c) 2021 by
9 # Permission to use, copy, modify, and/or distribute this software
10 # for any purpose with or without fee is hereby granted, provided
11 # that the above copyright notice and this permission notice appear
14 # THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
15 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
17 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
18 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
20 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
25 test_ospf_sr_te_topo1.py:
37 +---------+ | +---------+
39 | RT2 |eth-sw1 | eth-sw1| RT3 |
40 | 2.2.2.2 +----------+ + 3.3.3.3 |
42 +---------+ +---------+
43 eth-rt4-1| eth-rt5-1| |eth-rt5-2
45 10.0.2.0/24| 10.0.4.0/24| |10.0.5.0/24
47 eth-rt2-1| eth-rt3-1| |eth-rt3-2
48 +---------+ +---------+
50 | RT4 | 10.0.6.0/24 | RT5 |
51 | 4.4.4.4 +---------------------+ 5.5.5.5 |
53 +---------+ +---------+
56 10.0.7.0/24| |10.0.8.0/24
60 +----------+ 6.6.6.6 +-----------+
81 from time
import sleep
82 from functools
import partial
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
, "../"))
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
94 # Required to instantiate the topology builder class.
96 pytestmark
= [pytest
.mark
.bgpd
, pytest
.mark
.ospfd
, pytest
.mark
.pathd
]
105 for router
in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "dst"]:
106 tgen
.add_router(router
)
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")
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")
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")
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")
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")
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")
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")
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")
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")
149 def setup_module(mod
):
150 "Sets up the pytest environment"
152 tgen
= Topogen(build_topo
, mod
.__name
__)
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 in:" + frrdir
)
158 tgen
.start_topology()
160 router_list
= tgen
.routers()
162 # For all registered routers, load the zebra configuration file
163 for rname
, router
in router_list
.items():
165 TopoRouter
.RD_ZEBRA
, os
.path
.join(CWD
, "{}/zebra.conf".format(rname
))
168 TopoRouter
.RD_OSPF
, os
.path
.join(CWD
, "{}/ospfd.conf".format(rname
))
171 TopoRouter
.RD_PATH
, os
.path
.join(CWD
, "{}/pathd.conf".format(rname
))
174 TopoRouter
.RD_BGP
, os
.path
.join(CWD
, "{}/bgpd.conf".format(rname
))
180 def teardown_module(mod
):
181 "Teardown the pytest environment"
184 # This function tears down the whole topology.
188 def setup_testcase(msg
):
192 # Skip if previous fatal error condition is raised
193 if tgen
.routers_have_failure():
194 pytest
.skip(tgen
.errors
)
199 def print_cmd_result(rname
, command
):
200 print(get_topogen().gears
[rname
].vtysh_cmd(command
, isjson
=False))
203 def compare_json_test(router
, command
, reference
, exact
):
204 output
= router
.vtysh_cmd(command
, isjson
=True)
205 result
= topotest
.json_cmp(output
, reference
)
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
)
215 def cmp_json_output(rname
, command
, reference
, exact
=False):
216 "Compare router JSON output"
218 logger
.info('Comparing router "%s" "%s" output', rname
, command
)
221 filename
= "{}/{}/{}".format(CWD
, rname
, reference
)
222 expected
= json
.loads(open(filename
).read())
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
=60, wait
=0.5)
227 assertmsg
= '"{}" JSON output mismatches the expected result'.format(rname
)
228 assert diff
is None, assertmsg
231 def cmp_json_output_exact(rname
, command
, reference
):
232 return cmp_json_output(rname
, command
, reference
, True)
235 def add_candidate_path(rname
, endpoint
, pref
, name
, segment_list
="default"):
236 get_topogen().net
[rname
].cmd(
239 -c "segment-routing" \
241 -c "policy color 1 endpoint """
244 -c "candidate-path preference """
248 + """ explicit segment-list """
254 def delete_candidate_path(rname
, endpoint
, pref
):
255 get_topogen().net
[rname
].cmd(
258 -c "segment-routing" \
260 -c "policy color 1 endpoint """
263 -c "no candidate-path preference """
269 def add_segment(rname
, name
, index
, label
):
270 get_topogen().net
[rname
].cmd(
273 -c "segment-routing" \
286 def delete_segment(rname
, name
, index
):
287 get_topogen().net
[rname
].cmd(
290 -c "segment-routing" \
301 def add_segment_adj(rname
, name
, index
, src
, dst
):
302 get_topogen().net
[rname
].cmd(
305 -c "segment-routing" \
312 + """ nai adjacency """
320 def create_sr_policy(rname
, endpoint
, bsid
):
321 get_topogen().net
[rname
].cmd(
324 -c "segment-routing" \
326 -c "policy color 1 endpoint """
336 def delete_sr_policy(rname
, endpoint
):
337 get_topogen().net
[rname
].cmd(
340 -c "segment-routing" \
342 -c "no policy color 1 endpoint """
348 def create_prefix_sid(rname
, prefix
, sid
):
349 get_topogen().net
[rname
].cmd(
353 -c "segment-routing prefix """
361 def delete_prefix_sid(rname
, prefix
):
362 get_topogen().net
[rname
].cmd(
366 -c "no segment-routing prefix "'''
371 def check_bsid(rt
, bsid
, fn_name
, positive
):
373 Search for a bsid in rt1 and rt6
374 Positive means that check is true is bsid is found
375 Positive="False" means that check is true is bsid is NOT found
378 logger
.info('Checking "%s" bsid "%s" for router "%s" ', positive
, bsid
, rt
)
382 candidate_output
= ""
383 # First wait for convergence
390 router
= tgen
.gears
[rt
]
391 candidate_output
= router
.vtysh_cmd("show mpls table json")
392 candidate_output_json
= json
.loads(candidate_output
)
393 for item
in candidate_output_json
.items():
394 # logger.info('item "%s"', item)
395 if item
[0] == candidate_key
:
402 assertmsg
= "{} don't has entry {} but is was expected".format(
403 router
.name
, candidate_key
408 assertmsg
= "{} has entry {} but is wans't expected".format(
409 router
.name
, candidate_key
412 logger
.info('Success "%s" in "%s"', router
.name
, fn_name
)
414 assert matched
, assertmsg
420 # Checking the MPLS table using a single SR Policy and a single Candidate Path
421 # Segment list are base in adjacency that query TED
423 def test_srte_init_step1():
424 setup_testcase("Test (step 1): wait OSPF convergence / label distribution")
426 check_bsid("rt1", "1111", test_srte_init_step1
.__name__
, False)
427 check_bsid("rt6", "6666", test_srte_init_step1
.__name__
, False)
430 def test_srte_add_candidate_check_mpls_table_step1():
431 setup_testcase("Test (step 1): check MPLS table regarding the added Candidate Path")
433 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
434 add_candidate_path(rname
, endpoint
, 100, "default")
437 "1111" if rname
== "rt1" else "6666",
438 test_srte_init_step1
.__name__
,
441 delete_candidate_path(rname
, endpoint
, 100)
444 def test_srte_reinstall_sr_policy_check_mpls_table_step1():
446 "Test (step 1): check MPLS table after the SR Policy was removed and reinstalled"
449 for rname
, endpoint
, bsid
in [("rt1", "6.6.6.6", 1111), ("rt6", "1.1.1.1", 6666)]:
450 add_candidate_path(rname
, endpoint
, 100, "default")
451 delete_sr_policy(rname
, endpoint
)
452 check_bsid(rname
, bsid
, test_srte_init_step1
.__name__
, False)
453 create_sr_policy(rname
, endpoint
, bsid
)
454 add_candidate_path(rname
, endpoint
, 100, "default")
457 "1111" if rname
== "rt1" else "6666",
458 test_srte_init_step1
.__name__
,
461 delete_candidate_path(rname
, endpoint
, 100)
467 # Checking pathd operational data using a single SR Policy and a single Candidate Path
468 # Segment list are base in adjacency that query TED
470 def test_srte_bare_policy_step2():
471 setup_testcase("Test (step 2): bare SR Policy should not be operational")
473 for rname
in ["rt1", "rt6"]:
474 cmp_json_output_exact(
476 "show yang operational-data /frr-pathd:pathd pathd",
477 "step2/show_operational_data.ref",
481 def test_srte_add_candidate_check_operational_data_step2():
483 "Test (step 2): add single Candidate Path, SR Policy should be operational"
486 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
487 add_candidate_path(rname
, endpoint
, 100, "default")
490 "show yang operational-data /frr-pathd:pathd pathd",
491 "step2/show_operational_data_with_candidate.ref",
495 def test_srte_config_remove_candidate_check_operational_data_step2():
497 "Test (step 2): remove single Candidate Path, SR Policy should not be operational anymore"
500 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
501 delete_candidate_path(rname
, endpoint
, 100)
502 cmp_json_output_exact(
504 "show yang operational-data /frr-pathd:pathd pathd",
505 "step2/show_operational_data.ref",
512 # Testing the Candidate Path selection
513 # Segment list are based in adjacencies resolved by query TED
515 def test_srte_add_two_candidates_step3():
516 setup_testcase("Test (step 3): second Candidate Path has higher Priority")
518 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
519 for pref
, cand_name
in [("100", "first"), ("200", "second")]:
520 add_candidate_path(rname
, endpoint
, pref
, cand_name
)
523 "show yang operational-data /frr-pathd:pathd pathd",
524 "step3/show_operational_data_with_two_candidates.ref",
528 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
529 for pref
in ["100", "200"]:
530 delete_candidate_path(rname
, endpoint
, pref
)
533 def test_srte_add_two_candidates_with_reverse_priority_step3():
534 setup_testcase("Test (step 3): second Candidate Path has lower Priority")
536 # Use reversed priorities here
537 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
538 for pref
, cand_name
in [("200", "first"), ("100", "second")]:
539 add_candidate_path(rname
, endpoint
, pref
, cand_name
)
542 "show yang operational-data /frr-pathd:pathd pathd",
543 "step3/show_operational_data_with_two_candidates.ref",
547 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
548 for pref
in ["100", "200"]:
549 delete_candidate_path(rname
, endpoint
, pref
)
552 def test_srte_remove_best_candidate_step3():
553 setup_testcase("Test (step 3): delete the Candidate Path with higher priority")
555 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
556 for pref
, cand_name
in [("100", "first"), ("200", "second")]:
557 add_candidate_path(rname
, endpoint
, pref
, cand_name
)
559 # Delete candidate with higher priority
560 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
561 delete_candidate_path(rname
, endpoint
, 200)
563 # Candidate with lower priority should get active now
564 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
567 "show yang operational-data /frr-pathd:pathd pathd",
568 "step3/show_operational_data_with_single_candidate.ref",
571 delete_candidate_path(rname
, endpoint
, 100)
577 # Checking MPLS table with a single SR Policy and a Candidate Path with different Segment Lists and other modifications
578 # Segment list are base in adjacency that query TED
580 def test_srte_change_segment_list_check_mpls_table_step4():
581 setup_testcase("Test (step 4): check MPLS table for changed Segment List")
583 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
584 add_candidate_path(rname
, endpoint
, 100, "default")
585 # now change the segment list name
586 add_candidate_path(rname
, endpoint
, 100, "default", "test")
589 "1111" if rname
== "rt1" else "6666",
590 test_srte_init_step1
.__name__
,
593 delete_segment(rname
, "test", 10)
594 delete_segment(rname
, "test", 20)
595 delete_segment(rname
, "test", 30)
596 delete_segment(rname
, "test", 40)
598 add_segment_adj(rname
, "test", 10, "10.0.1.1", "10.0.1.2")
599 add_segment_adj(rname
, "test", 20, "10.0.2.2", "10.0.2.4")
600 add_segment_adj(rname
, "test", 30, "10.0.6.4", "10.0.6.5")
601 add_segment_adj(rname
, "test", 40, "10.0.8.5", "10.0.8.6")
603 add_segment_adj(rname
, "test", 10, "10.0.8.6", "10.0.8.5")
604 add_segment_adj(rname
, "test", 20, "10.0.6.5", "10.0.6.4")
605 add_segment_adj(rname
, "test", 30, "10.0.2.4", "10.0.2.2")
606 add_segment_adj(rname
, "test", 40, "10.0.1.2", "10.0.1.1")
609 "1111" if rname
== "rt1" else "6666",
610 test_srte_init_step1
.__name__
,
613 delete_candidate_path(rname
, endpoint
, 100)
616 def test_srte_change_sl_priority_error_ted_check_mpls_table_step4():
617 setup_testcase("Test (step 4): check MPLS table keeps low prio sl")
619 for rname
, endpoint
in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
620 add_candidate_path(rname
, endpoint
, 100, "default")
621 # now change the segment list name
622 add_candidate_path(rname
, endpoint
, 200, "test", "test")
625 "1111" if rname
== "rt1" else "6666",
626 test_srte_init_step1
.__name__
,
629 delete_segment(rname
, "test", 10)
630 delete_segment(rname
, "test", 20)
631 delete_segment(rname
, "test", 30)
632 delete_segment(rname
, "test", 40)
635 add_segment_adj(rname
, "test", 10, "10.0.1.99", "10.0.1.99")
636 add_segment_adj(rname
, "test", 20, "10.0.2.99", "10.0.2.99")
637 add_segment_adj(rname
, "test", 30, "10.0.6.99", "10.0.6.99")
638 add_segment_adj(rname
, "test", 40, "10.0.8.99", "10.0.8.99")
640 add_segment_adj(rname
, "test", 10, "10.0.8.99", "10.0.8.99")
641 add_segment_adj(rname
, "test", 20, "10.0.6.99", "10.0.6.99")
642 add_segment_adj(rname
, "test", 30, "10.0.2.99", "10.0.2.99")
643 add_segment_adj(rname
, "test", 40, "10.0.1.99", "10.0.1.99")
644 # So policy sticks with default sl even higher prio
647 "1111" if rname
== "rt1" else "6666",
648 test_srte_init_step1
.__name__
,
651 delete_candidate_path(rname
, endpoint
, 100)
654 # Memory leak test template
655 def test_memory_leak():
656 "Run the memory leak test and report results."
658 if not tgen
.is_memleak_enabled():
659 pytest
.skip("Memory leak test/report is disabled")
661 tgen
.report_memory_leaks()
664 if __name__
== "__main__":
665 args
= ["-s"] + sys
.argv
[1:]
666 sys
.exit(pytest
.main(args
))