]>
git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py
4 # test_isis_sr_topo1.py
5 # Part of NetDEF Topology Tests
7 # Copyright (c) 2019 by
8 # Network Device Education Foundation, Inc. ("NetDEF")
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
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
26 test_isis_sr_te_topo1.py:
38 +---------+ | +---------+
40 | RT2 |eth-sw1 | eth-sw1| RT3 |
41 | 2.2.2.2 +----------+----------+ 3.3.3.3 |
43 +---------+ +---------+
44 eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2
46 10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24
48 eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2
49 +---------+ +---------+
51 | RT4 | 10.0.6.0/24 | RT5 |
52 | 4.4.4.4 +---------------------+ 5.5.5.5 |
54 +---------+ +---------+
57 10.0.7.0/24| |10.0.8.0/24
61 +----------+ 6.6.6.6 +-----------+
83 from time
import sleep
84 from functools
import partial
86 # Save the Current Working Directory to find configuration files.
87 CWD
= os
.path
.dirname(os
.path
.realpath(__file__
))
88 sys
.path
.append(os
.path
.join(CWD
, '../'))
90 # pylint: disable=C0413
91 # Import topogen and topotest helpers
92 from lib
import topotest
93 from lib
.topogen
import Topogen
, TopoRouter
, get_topogen
94 from lib
.topolog
import logger
96 # Required to instantiate the topology builder class.
97 from mininet
.topo
import Topo
99 class TemplateTopo(Topo
):
100 "Test topology builder"
101 def build(self
, *_args
, **_opts
):
103 tgen
= get_topogen(self
)
108 for router
in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'dst']:
109 tgen
.add_router(router
)
114 switch
= tgen
.add_switch('s1')
115 switch
.add_link(tgen
.gears
['rt1'], nodeif
="eth-sw1")
116 switch
.add_link(tgen
.gears
['rt2'], nodeif
="eth-sw1")
117 switch
.add_link(tgen
.gears
['rt3'], nodeif
="eth-sw1")
119 switch
= tgen
.add_switch('s2')
120 switch
.add_link(tgen
.gears
['rt2'], nodeif
="eth-rt4-1")
121 switch
.add_link(tgen
.gears
['rt4'], nodeif
="eth-rt2-1")
123 switch
= tgen
.add_switch('s3')
124 switch
.add_link(tgen
.gears
['rt2'], nodeif
="eth-rt4-2")
125 switch
.add_link(tgen
.gears
['rt4'], nodeif
="eth-rt2-2")
127 switch
= tgen
.add_switch('s4')
128 switch
.add_link(tgen
.gears
['rt3'], nodeif
="eth-rt5-1")
129 switch
.add_link(tgen
.gears
['rt5'], nodeif
="eth-rt3-1")
131 switch
= tgen
.add_switch('s5')
132 switch
.add_link(tgen
.gears
['rt3'], nodeif
="eth-rt5-2")
133 switch
.add_link(tgen
.gears
['rt5'], nodeif
="eth-rt3-2")
135 switch
= tgen
.add_switch('s6')
136 switch
.add_link(tgen
.gears
['rt4'], nodeif
="eth-rt5")
137 switch
.add_link(tgen
.gears
['rt5'], nodeif
="eth-rt4")
139 switch
= tgen
.add_switch('s7')
140 switch
.add_link(tgen
.gears
['rt4'], nodeif
="eth-rt6")
141 switch
.add_link(tgen
.gears
['rt6'], nodeif
="eth-rt4")
143 switch
= tgen
.add_switch('s8')
144 switch
.add_link(tgen
.gears
['rt5'], nodeif
="eth-rt6")
145 switch
.add_link(tgen
.gears
['rt6'], nodeif
="eth-rt5")
147 switch
= tgen
.add_switch('s9')
148 switch
.add_link(tgen
.gears
['rt6'], nodeif
="eth-dst")
149 switch
.add_link(tgen
.gears
['dst'], nodeif
="eth-rt6")
151 def setup_module(mod
):
152 "Sets up the pytest environment"
153 tgen
= Topogen(TemplateTopo
, mod
.__name
__)
154 tgen
.start_topology()
156 router_list
= tgen
.routers()
158 # For all registered routers, load the zebra configuration file
159 for rname
, router
in router_list
.iteritems():
162 os
.path
.join(CWD
, '{}/zebra.conf'.format(rname
))
166 os
.path
.join(CWD
, '{}/isisd.conf'.format(rname
))
170 os
.path
.join(CWD
, '{}/pathd.conf'.format(rname
))
174 os
.path
.join(CWD
, '{}/bgpd.conf'.format(rname
))
179 def teardown_module(mod
):
180 "Teardown the pytest environment"
183 # This function tears down the whole topology.
186 def setup_testcase(msg
):
190 # Skip if previous fatal error condition is raised
191 if tgen
.routers_have_failure():
192 pytest
.skip(tgen
.errors
)
196 def print_cmd_result(rname
, command
):
197 print(get_topogen().gears
[rname
].vtysh_cmd(command
, isjson
=False))
199 def compare_json_test(router
, command
, reference
, exact
):
200 output
= router
.vtysh_cmd(command
, isjson
=True)
201 result
= topotest
.json_cmp(output
, reference
)
203 # Note: topotest.json_cmp() just checks on inclusion of keys.
204 # For exact matching also compare the other way around.
205 if not result
and exact
:
206 return topotest
.json_cmp(reference
, output
)
210 def cmp_json_output(rname
, command
, reference
, exact
=False):
211 "Compare router JSON output"
213 logger
.info('Comparing router "%s" "%s" output', rname
, command
)
216 filename
= '{}/{}/{}'.format(CWD
, rname
, reference
)
217 expected
= json
.loads(open(filename
).read())
219 # Run test function until we get an result. Wait at most 60 seconds.
220 test_func
= partial(compare_json_test
,
221 tgen
.gears
[rname
], command
, expected
, exact
)
222 _
, diff
= topotest
.run_and_expect(test_func
, None, count
=120, wait
=0.5)
223 assertmsg
= '"{}" JSON output mismatches the expected result'.format(rname
)
224 assert diff
is None, assertmsg
226 def cmp_json_output_exact(rname
, command
, reference
):
227 return cmp_json_output(rname
, command
, reference
, True)
229 def add_candidate_path(rname
, endpoint
, pref
, name
, segment_list
='default'):
230 get_topogen().net
[rname
].cmd(''' \
232 -c "segment-routing" \
234 -c "policy color 1 endpoint ''' + endpoint
+ '''" \
235 -c "candidate-path preference ''' + str(pref
) + ''' name ''' + name
+ ''' explicit segment-list ''' + segment_list
+ '''"''')
237 def delete_candidate_path(rname
, endpoint
, pref
):
238 get_topogen().net
[rname
].cmd(''' \
240 -c "segment-routing" \
242 -c "policy color 1 endpoint ''' + endpoint
+ '''" \
243 -c "no candidate-path preference ''' + str(pref
) + '''"''')
245 def add_segment(rname
, name
, index
, label
):
246 get_topogen().net
[rname
].cmd(''' \
248 -c "segment-routing" \
250 -c "segment-list ''' + name
+ '''" \
251 -c "index ''' + str(index
) + ''' mpls label ''' + str(label
) + '''"''')
253 def delete_segment(rname
, name
, index
):
254 get_topogen().net
[rname
].cmd(''' \
256 -c "segment-routing" \
258 -c "segment-list ''' + name
+ '''" \
259 -c "no index ''' + str(index
) + '''"''')
261 def create_sr_policy(rname
, endpoint
, bsid
):
262 get_topogen().net
[rname
].cmd(''' \
264 -c "segment-routing" \
266 -c "policy color 1 endpoint ''' + endpoint
+ '''" \
268 -c "binding-sid ''' + str(bsid
) + '''"''')
270 def delete_sr_policy(rname
, endpoint
):
271 get_topogen().net
[rname
].cmd(''' \
273 -c "segment-routing" \
275 -c "no policy color 1 endpoint ''' + endpoint
+ '''"''')
277 def create_prefix_sid(rname
, prefix
, sid
):
278 get_topogen().net
[rname
].cmd(''' \
281 -c "segment-routing prefix ''' + prefix
+ " index " + str(sid
) + '''"''')
283 def delete_prefix_sid(rname
, prefix
):
284 get_topogen().net
[rname
].cmd(''' \
287 -c "no segment-routing prefix "''' + prefix
)
292 # Checking the MPLS table using a single SR Policy and a single Candidate Path
294 def test_srte_init_step1():
295 setup_testcase("Test (step 1): wait for IS-IS convergence / label distribution")
297 for rname
in ['rt1', 'rt6']:
298 cmp_json_output(rname
,
299 "show mpls table json",
300 "step1/show_mpls_table_without_candidate.ref")
302 def test_srte_add_candidate_check_mpls_table_step1():
303 setup_testcase("Test (step 1): check MPLS table regarding the added Candidate Path")
305 for rname
, endpoint
in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
306 add_candidate_path(rname
, endpoint
, 100, 'default')
307 cmp_json_output(rname
,
308 "show mpls table json",
309 "step1/show_mpls_table_with_candidate.ref")
310 delete_candidate_path(rname
, endpoint
, 100)
312 def test_srte_reinstall_sr_policy_check_mpls_table_step1():
313 setup_testcase("Test (step 1): check MPLS table after the SR Policy was removed and reinstalled")
315 for rname
, endpoint
, bsid
in [('rt1', '6.6.6.6', 1111), ('rt6', '1.1.1.1', 6666)]:
316 add_candidate_path(rname
, endpoint
, 100, 'default')
317 delete_sr_policy(rname
, endpoint
)
318 cmp_json_output(rname
,
319 "show mpls table json",
320 "step1/show_mpls_table_without_candidate.ref")
321 create_sr_policy(rname
, endpoint
, bsid
)
322 add_candidate_path(rname
, endpoint
, 100, 'default')
323 cmp_json_output(rname
,
324 "show mpls table json",
325 "step1/show_mpls_table_with_candidate.ref")
326 delete_candidate_path(rname
, endpoint
, 100)
331 # Checking pathd operational data using a single SR Policy and a single Candidate Path
333 def test_srte_bare_policy_step2():
334 setup_testcase("Test (step 2): bare SR Policy should not be operational")
336 for rname
in ['rt1', 'rt6']:
337 cmp_json_output_exact(rname
,
338 "show yang operational-data /frr-pathd:pathd pathd",
339 "step2/show_operational_data.ref")
341 def test_srte_add_candidate_check_operational_data_step2():
342 setup_testcase("Test (step 2): add single Candidate Path, SR Policy should be operational")
344 for rname
, endpoint
in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
345 add_candidate_path(rname
, endpoint
, 100, 'default')
346 cmp_json_output(rname
,
347 "show yang operational-data /frr-pathd:pathd pathd",
348 "step2/show_operational_data_with_candidate.ref")
350 def test_srte_config_remove_candidate_check_operational_data_step2():
351 setup_testcase("Test (step 2): remove single Candidate Path, SR Policy should not be operational anymore")
353 for rname
, endpoint
in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
354 delete_candidate_path(rname
, endpoint
, 100)
355 cmp_json_output_exact(rname
,
356 "show yang operational-data /frr-pathd:pathd pathd",
357 "step2/show_operational_data.ref")
362 # Testing the Candidate Path selection
364 def test_srte_add_two_candidates_step3():
365 setup_testcase("Test (step 3): second Candidate Path has higher Priority")
367 for rname
, endpoint
in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
368 for pref
, cand_name
in [('100', 'first'), ('200', 'second')]:
369 add_candidate_path(rname
, endpoint
, pref
, cand_name
)
370 cmp_json_output(rname
,
371 "show yang operational-data /frr-pathd:pathd pathd",
372 "step3/show_operational_data_with_two_candidates.ref")
375 for rname
, endpoint
in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
376 for pref
in ['100', '200']:
377 delete_candidate_path(rname
, endpoint
, pref
)
379 def test_srte_add_two_candidates_with_reverse_priority_step3():
380 setup_testcase("Test (step 3): second Candidate Path has lower Priority")
382 # Use reversed priorities here
383 for rname
, endpoint
in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
384 for pref
, cand_name
in [('200', 'first'), ('100', 'second')]:
385 add_candidate_path(rname
, endpoint
, pref
, cand_name
)
386 cmp_json_output(rname
,
387 "show yang operational-data /frr-pathd:pathd pathd",
388 "step3/show_operational_data_with_two_candidates.ref")
391 for rname
, endpoint
in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
392 for pref
in ['100', '200']:
393 delete_candidate_path(rname
, endpoint
, pref
)
395 def test_srte_remove_best_candidate_step3():
396 setup_testcase("Test (step 3): delete the Candidate Path with higher priority")
398 for rname
, endpoint
in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
399 for pref
, cand_name
in [('100', 'first'), ('200', 'second')]:
400 add_candidate_path(rname
, endpoint
, pref
, cand_name
)
402 # Delete candidate with higher priority
403 for rname
, endpoint
in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
404 delete_candidate_path(rname
, endpoint
, 200)
406 # Candidate with lower priority should get active now
407 for rname
, endpoint
in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
408 cmp_json_output(rname
,
409 "show yang operational-data /frr-pathd:pathd pathd",
410 "step3/show_operational_data_with_single_candidate.ref")
412 delete_candidate_path(rname
, endpoint
, 100)
417 # Checking MPLS table with a single SR Policy and a Candidate Path with different Segment Lists and other modifications
419 def test_srte_change_segment_list_check_mpls_table_step4():
420 setup_testcase("Test (step 4): check MPLS table for changed Segment List")
422 for rname
, endpoint
in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
423 add_candidate_path(rname
, endpoint
, 100, 'default')
424 # now change the segment list name
425 add_candidate_path(rname
, endpoint
, 100, 'default', 'test')
426 cmp_json_output(rname
,
427 "show mpls table json",
428 "step4/show_mpls_table.ref")
429 delete_candidate_path(rname
, endpoint
, 100)
431 def test_srte_segment_list_add_segment_check_mpls_table_step4():
432 setup_testcase("Test (step 4): check MPLS table for added (then changed and finally deleted) segment")
434 add_candidate_path('rt1', '6.6.6.6', 100, 'default', 'test')
436 # first add a new segment
437 add_segment('rt1', 'test', 25, 16050)
438 cmp_json_output('rt1',
439 "show mpls table json",
440 "step4/show_mpls_table_add_segment.ref")
442 # ... then change it ...
443 add_segment('rt1', 'test', 25, 16030)
444 cmp_json_output('rt1',
445 "show mpls table json",
446 "step4/show_mpls_table_change_segment.ref")
448 # ... and finally delete it
449 delete_segment('rt1', 'test', 25)
450 cmp_json_output('rt1',
451 "show mpls table json",
452 "step4/show_mpls_table.ref")
453 delete_candidate_path('rt1', '6.6.6.6', 100)
458 # Checking the nexthop using a single SR Policy and a Candidate Path with configured route-map
460 def test_srte_route_map_with_sr_policy_check_nextop_step5():
461 setup_testcase("Test (step 5): recursive nexthop learned through BGP neighbour should be aligned with SR Policy from route-map")
463 # (re-)build the SR Policy two times to ensure that reinstalling still works
465 cmp_json_output('rt1',
466 "show ip route bgp json",
467 "step5/show_ip_route_bgp_inactive_srte.ref")
469 delete_sr_policy('rt1', '6.6.6.6')
470 cmp_json_output('rt1',
471 "show ip route bgp json",
472 "step5/show_ip_route_bgp_inactive_srte.ref")
474 create_sr_policy('rt1', '6.6.6.6', 1111)
475 cmp_json_output('rt1',
476 "show ip route bgp json",
477 "step5/show_ip_route_bgp_inactive_srte.ref")
479 add_candidate_path('rt1', '6.6.6.6', 100, 'default')
480 cmp_json_output('rt1',
481 "show ip route bgp json",
482 "step5/show_ip_route_bgp_active_srte.ref")
484 delete_candidate_path('rt1', '6.6.6.6', 100)
486 def test_srte_route_map_with_sr_policy_reinstall_prefix_sid_check_nextop_step5():
487 setup_testcase("Test (step 5): remove and re-install prefix SID on fist path element and check SR Policy activity")
489 # first add a candidate path so the SR Policy is active
490 add_candidate_path('rt1', '6.6.6.6', 100, 'default')
491 cmp_json_output('rt1',
492 "show yang operational-data /frr-pathd:pathd pathd",
493 "step5/show_operational_data_active.ref")
495 # delete prefix SID from first element of the configured path and check
496 # if the SR Policy is inactive since the label can't be resolved anymore
497 delete_prefix_sid('rt5', "5.5.5.5/32")
498 cmp_json_output('rt1',
499 "show yang operational-data /frr-pathd:pathd pathd",
500 "step5/show_operational_data_inactive.ref")
501 cmp_json_output('rt1',
502 "show ip route bgp json",
503 "step5/show_ip_route_bgp_inactive_srte.ref")
505 # re-create the prefix SID and check if the SR Policy is active
506 create_prefix_sid('rt5', "5.5.5.5/32", 50)
507 cmp_json_output('rt1',
508 "show yang operational-data /frr-pathd:pathd pathd",
509 "step5/show_operational_data_active.ref")
510 cmp_json_output('rt1',
511 "show ip route bgp json",
512 "step5/show_ip_route_bgp_active_srte.ref")
514 # Memory leak test template
515 def test_memory_leak():
516 "Run the memory leak test and report results."
518 if not tgen
.is_memleak_enabled():
519 pytest
.skip('Memory leak test/report is disabled')
521 tgen
.report_memory_leaks()
523 if __name__
== '__main__':
524 args
= ["-s"] + sys
.argv
[1:]
525 sys
.exit(pytest
.main(args
))