]>
git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
4 # test_ospf_gr_topo1.py
5 # Part of NetDEF Topology Tests
7 # Copyright (c) 2021 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_ospf_gr_topo1.py:
52 10.0.3.0/24 | | 10.0.4.0/24
53 +---------+ +--------+
56 +---------+ +---------+
58 | 4.4.4.4 | | 6.6.6.6 |
59 +---------+ +---------+
62 |10.0.5.0/24 |10.0.6.0/24
65 +---------+ +---------+
67 | 5.5.5.5 | | 7.7.7.7 |
68 +---------+ +---------+
75 from time
import sleep
76 from functools
import partial
78 # Save the Current Working Directory to find configuration files.
79 CWD
= os
.path
.dirname(os
.path
.realpath(__file__
))
80 sys
.path
.append(os
.path
.join(CWD
, "../"))
82 # pylint: disable=C0413
83 # Import topogen and topotest helpers
84 from lib
import topotest
85 from lib
.topogen
import Topogen
, TopoRouter
, get_topogen
86 from lib
.topolog
import logger
87 from lib
.common_config
import (
92 # Required to instantiate the topology builder class.
94 pytestmark
= [pytest
.mark
.ospfd
]
96 # Global multi-dimensional dictionary containing all expected outputs
100 def build_topo(tgen
):
104 for router
in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
105 tgen
.add_router(router
)
110 switch
= tgen
.add_switch("s1")
111 switch
.add_link(tgen
.gears
["rt1"], nodeif
="eth-rt2")
112 switch
.add_link(tgen
.gears
["rt2"], nodeif
="eth-rt1")
114 switch
= tgen
.add_switch("s2")
115 switch
.add_link(tgen
.gears
["rt1"], nodeif
="stub1")
117 switch
= tgen
.add_switch("s3")
118 switch
.add_link(tgen
.gears
["rt2"], nodeif
="eth-rt3")
119 switch
.add_link(tgen
.gears
["rt3"], nodeif
="eth-rt2")
121 switch
= tgen
.add_switch("s4")
122 switch
.add_link(tgen
.gears
["rt3"], nodeif
="eth-rt4")
123 switch
.add_link(tgen
.gears
["rt4"], nodeif
="eth-rt3")
125 switch
= tgen
.add_switch("s5")
126 switch
.add_link(tgen
.gears
["rt3"], nodeif
="eth-rt6")
127 switch
.add_link(tgen
.gears
["rt6"], nodeif
="eth-rt3")
129 switch
= tgen
.add_switch("s6")
130 switch
.add_link(tgen
.gears
["rt4"], nodeif
="eth-rt5")
131 switch
.add_link(tgen
.gears
["rt5"], nodeif
="eth-rt4")
133 switch
= tgen
.add_switch("s7")
134 switch
.add_link(tgen
.gears
["rt6"], nodeif
="eth-rt7")
135 switch
.add_link(tgen
.gears
["rt7"], nodeif
="eth-rt6")
137 switch
= tgen
.add_switch("s8")
138 switch
.add_link(tgen
.gears
["rt7"], nodeif
="stub1")
141 def setup_module(mod
):
142 "Sets up the pytest environment"
143 tgen
= Topogen(build_topo
, mod
.__name
__)
144 tgen
.start_topology()
146 router_list
= tgen
.routers()
148 # For all registered routers, load the zebra configuration file
149 for rname
, router
in router_list
.items():
151 TopoRouter
.RD_ZEBRA
, os
.path
.join(CWD
, "{}/zebra.conf".format(rname
))
154 TopoRouter
.RD_OSPF
, os
.path
.join(CWD
, "{}/ospfd.conf".format(rname
))
160 def teardown_module(mod
):
161 "Teardown the pytest environment"
164 # This function tears down the whole topology.
168 def router_compare_json_output(rname
, command
, reference
, tries
):
169 "Compare router JSON output"
171 logger
.info('Comparing router "%s" "%s" output', rname
, command
)
174 filename
= "{}/{}/{}".format(CWD
, rname
, reference
)
175 expected
= json
.loads(open(filename
).read())
177 test_func
= partial(topotest
.router_json_cmp
, tgen
.gears
[rname
], command
, expected
)
178 _
, diff
= topotest
.run_and_expect(test_func
, None, count
=tries
, wait
=0.5)
179 assertmsg
= '"{}" JSON output mismatches the expected result'.format(rname
)
180 assert diff
is None, assertmsg
183 def expect_grace_lsa(restarting
, area
, helper
):
185 Check if the given helper neighbor has already received a Grace-LSA from
186 the router performing a graceful restart.
191 "'{}': checking if a Grace-LSA was received from '{}'".format(
196 topotest
.router_json_cmp
,
198 "show ip ospf database opaque-link json",
200 "linkLocalOpaqueLsa": {
204 "advertisingRouter": restarting
,
205 "opaqueType": "Grace-LSA",
212 _
, result
= topotest
.run_and_expect(test_func
, None, count
=60, wait
=1)
213 assertmsg
= '"{}" didn\'t receive a Grace-LSA from "{}"'.format(helper
, restarting
)
215 assert result
is None, assertmsg
218 def check_routers(initial_convergence
=False, exiting
=None, restarting
=None):
219 for rname
in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
220 # Check the RIB first, which should be preserved across restarts in
221 # all routers of the routing domain.
222 # If we are not on initial convergence *but* we are checking
223 # after a restart. Looking in the zebra rib for installed
224 # is a recipe for test failure. Why? because if we are restarting
225 # then ospf is in the process of establishing neighbors and passing
226 # new routes to zebra. Zebra will not mark the route as installed
227 # when it receives a replacement from ospf until it has finished
228 # processing it. Let's give it a few seconds to allow this to happen
230 if initial_convergence
== True:
233 if restarting
!= None:
237 router_compare_json_output(
238 rname
, "show ip route ospf json", "show_ip_route.json", tries
241 # Check that all adjacencies are up and running (except when there's
242 # an OSPF instance that is shutting down).
245 router_compare_json_output(
246 rname
, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json", tries
249 # Check the OSPF RIB and LSDB.
250 # In the restarting router, wait up to one minute for the LSDB to converge.
252 if initial_convergence
== True or restarting
== rname
:
256 router_compare_json_output(
257 rname
, "show ip ospf database json", "show_ip_ospf_database.json", tries
259 router_compare_json_output(
260 rname
, "show ip ospf route json", "show_ip_ospf_route.json", tries
264 def ensure_gr_is_in_zebra(rname
):
269 while retry
and retry_times
> 0:
270 out
= tgen
.net
[rname
].cmd(
271 'vtysh -c "show zebra client" | grep "Client: ospf$" -A 40 | grep "Capabilities "'
274 if "Graceful Restart" not in out
:
280 assertmsg
= "%s does not appear to have Graceful Restart setup" % rname
281 assert not retry
and retry_times
> 0, assertmsg
285 # Test initial network convergence
287 def test_initial_convergence():
288 logger
.info("Test: verify initial network convergence")
291 # Skip if previous fatal error condition is raised
292 if tgen
.routers_have_failure():
293 pytest
.skip(tgen
.errors
)
295 check_routers(initial_convergence
=True)
299 # Test rt1 performing a graceful restart
302 logger
.info("Test: verify rt1 performing a graceful restart")
305 # Skip if previous fatal error condition is raised
306 if tgen
.routers_have_failure():
307 pytest
.skip(tgen
.errors
)
309 tgen
.net
["rt1"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
310 expect_grace_lsa(restarting
="1.1.1.1", area
="0.0.0.1", helper
="rt2")
311 ensure_gr_is_in_zebra("rt1")
312 kill_router_daemons(tgen
, "rt1", ["ospfd"], save_config
=False)
313 check_routers(exiting
="rt1")
315 start_router_daemons(tgen
, "rt1", ["ospfd"])
316 check_routers(restarting
="rt1")
320 # Test rt2 performing a graceful restart
323 logger
.info("Test: verify rt2 performing a graceful restart")
326 # Skip if previous fatal error condition is raised
327 if tgen
.routers_have_failure():
328 pytest
.skip(tgen
.errors
)
330 tgen
.net
["rt2"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
331 expect_grace_lsa(restarting
="2.2.2.2", area
="0.0.0.1", helper
="rt1")
332 expect_grace_lsa(restarting
="2.2.2.2", area
="0.0.0.0", helper
="rt3")
333 ensure_gr_is_in_zebra("rt2")
334 kill_router_daemons(tgen
, "rt2", ["ospfd"], save_config
=False)
335 check_routers(exiting
="rt2")
337 start_router_daemons(tgen
, "rt2", ["ospfd"])
338 check_routers(restarting
="rt2")
342 # Test rt3 performing a graceful restart
345 logger
.info("Test: verify rt3 performing a graceful restart")
348 # Skip if previous fatal error condition is raised
349 if tgen
.routers_have_failure():
350 pytest
.skip(tgen
.errors
)
352 tgen
.net
["rt3"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
353 expect_grace_lsa(restarting
="3.3.3.3", area
="0.0.0.0", helper
="rt2")
354 expect_grace_lsa(restarting
="3.3.3.3", area
="0.0.0.0", helper
="rt4")
355 expect_grace_lsa(restarting
="3.3.3.3", area
="0.0.0.0", helper
="rt6")
356 ensure_gr_is_in_zebra("rt3")
357 kill_router_daemons(tgen
, "rt3", ["ospfd"], save_config
=False)
358 check_routers(exiting
="rt3")
360 start_router_daemons(tgen
, "rt3", ["ospfd"])
361 check_routers(restarting
="rt3")
365 # Test rt4 performing a graceful restart
368 logger
.info("Test: verify rt4 performing a graceful restart")
371 # Skip if previous fatal error condition is raised
372 if tgen
.routers_have_failure():
373 pytest
.skip(tgen
.errors
)
375 tgen
.net
["rt4"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
376 expect_grace_lsa(restarting
="4.4.4.4", area
="0.0.0.0", helper
="rt3")
377 expect_grace_lsa(restarting
="4.4.4.4", area
="0.0.0.2", helper
="rt5")
378 ensure_gr_is_in_zebra("rt4")
379 kill_router_daemons(tgen
, "rt4", ["ospfd"], save_config
=False)
380 check_routers(exiting
="rt4")
382 start_router_daemons(tgen
, "rt4", ["ospfd"])
383 check_routers(restarting
="rt4")
387 # Test rt5 performing a graceful restart
390 logger
.info("Test: verify rt5 performing a graceful restart")
393 # Skip if previous fatal error condition is raised
394 if tgen
.routers_have_failure():
395 pytest
.skip(tgen
.errors
)
397 tgen
.net
["rt5"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
398 expect_grace_lsa(restarting
="5.5.5.5", area
="0.0.0.2", helper
="rt4")
399 ensure_gr_is_in_zebra("rt5")
400 kill_router_daemons(tgen
, "rt5", ["ospfd"], save_config
=False)
401 check_routers(exiting
="rt5")
403 start_router_daemons(tgen
, "rt5", ["ospfd"])
404 check_routers(restarting
="rt5")
408 # Test rt6 performing a graceful restart
411 logger
.info("Test: verify rt6 performing a graceful restart")
414 # Skip if previous fatal error condition is raised
415 if tgen
.routers_have_failure():
416 pytest
.skip(tgen
.errors
)
418 tgen
.net
["rt6"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
419 expect_grace_lsa(restarting
="6.6.6.6", area
="0.0.0.0", helper
="rt3")
420 expect_grace_lsa(restarting
="6.6.6.6", area
="0.0.0.3", helper
="rt7")
421 ensure_gr_is_in_zebra("rt6")
422 kill_router_daemons(tgen
, "rt6", ["ospfd"], save_config
=False)
423 check_routers(exiting
="rt6")
425 start_router_daemons(tgen
, "rt6", ["ospfd"])
426 check_routers(restarting
="rt6")
430 # Test rt7 performing a graceful restart
433 logger
.info("Test: verify rt7 performing a graceful restart")
436 # Skip if previous fatal error condition is raised
437 if tgen
.routers_have_failure():
438 pytest
.skip(tgen
.errors
)
440 tgen
.net
["rt7"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
441 expect_grace_lsa(restarting
="7.7.7.7", area
="0.0.0.3", helper
="rt6")
442 ensure_gr_is_in_zebra("rt7")
443 kill_router_daemons(tgen
, "rt7", ["ospfd"], save_config
=False)
444 check_routers(exiting
="rt7")
446 start_router_daemons(tgen
, "rt7", ["ospfd"])
447 check_routers(restarting
="rt7")
450 # Memory leak test template
451 def test_memory_leak():
452 "Run the memory leak test and report results."
454 if not tgen
.is_memleak_enabled():
455 pytest
.skip("Memory leak test/report is disabled")
457 tgen
.report_memory_leaks()
460 if __name__
== "__main__":
461 args
= ["-s"] + sys
.argv
[1:]
462 sys
.exit(pytest
.main(args
))