]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
Merge pull request #9883 from pguibert6WIND/iface_deleted_omitted_gre_tundest
[mirror_frr.git] / tests / topotests / ospf_gr_topo1 / test_ospf_gr_topo1.py
1 #!/usr/bin/env python
2
3 #
4 # test_ospf_gr_topo1.py
5 # Part of NetDEF Topology Tests
6 #
7 # Copyright (c) 2021 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_ospf_gr_topo1.py:
27
28 +---------+
29 | RT1 |
30 | 1.1.1.1 |
31 +---------+
32 |eth-rt2
33 |
34 |10.0.1.0/24
35 |
36 |eth-rt1
37 +---------+
38 | RT2 |
39 | 2.2.2.2 |
40 +---------+
41 |eth-rt3
42 |
43 |10.0.2.0/24
44 |
45 |eth-rt2
46 +---------+
47 | RT3 |
48 | 3.3.3.3 |
49 +---------+
50 eth-rt4| |eth-rt6
51 | |
52 10.0.3.0/24 | | 10.0.4.0/24
53 +---------+ +--------+
54 | |
55 |eth-rt3 |eth-rt3
56 +---------+ +---------+
57 | RT4 | | RT6 |
58 | 4.4.4.4 | | 6.6.6.6 |
59 +---------+ +---------+
60 |eth-rt5 |eth-rt7
61 | |
62 |10.0.5.0/24 |10.0.6.0/24
63 | |
64 |eth-rt4 |eth-rt6
65 +---------+ +---------+
66 | RT5 | | RT7 |
67 | 5.5.5.5 | | 7.7.7.7 |
68 +---------+ +---------+
69 """
70
71 import os
72 import sys
73 import pytest
74 import json
75 from time import sleep
76 from functools import partial
77
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, "../"))
81
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 (
88 kill_router_daemons,
89 start_router_daemons,
90 )
91
92 # Required to instantiate the topology builder class.
93
94 pytestmark = [pytest.mark.ospfd]
95
96 # Global multi-dimensional dictionary containing all expected outputs
97 outputs = {}
98
99
100 def build_topo(tgen):
101 #
102 # Define FRR Routers
103 #
104 for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
105 tgen.add_router(router)
106
107 #
108 # Define connections
109 #
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")
113
114 switch = tgen.add_switch("s2")
115 switch.add_link(tgen.gears["rt1"], nodeif="stub1")
116
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")
120
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")
124
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")
128
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")
132
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")
136
137 switch = tgen.add_switch("s8")
138 switch.add_link(tgen.gears["rt7"], nodeif="stub1")
139
140
141 def setup_module(mod):
142 "Sets up the pytest environment"
143 tgen = Topogen(build_topo, mod.__name__)
144 tgen.start_topology()
145
146 router_list = tgen.routers()
147
148 # For all registered routers, load the zebra configuration file
149 for rname, router in router_list.items():
150 router.load_config(
151 TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
152 )
153 router.load_config(
154 TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
155 )
156
157 tgen.start_router()
158
159
160 def teardown_module(mod):
161 "Teardown the pytest environment"
162 tgen = get_topogen()
163
164 # This function tears down the whole topology.
165 tgen.stop_topology()
166
167
168 def router_compare_json_output(rname, command, reference, tries):
169 "Compare router JSON output"
170
171 logger.info('Comparing router "%s" "%s" output', rname, command)
172
173 tgen = get_topogen()
174 filename = "{}/{}/{}".format(CWD, rname, reference)
175 expected = json.loads(open(filename).read())
176
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
181
182
183 def expect_grace_lsa(restarting, area, helper):
184 """
185 Check if the given helper neighbor has already received a Grace-LSA from
186 the router performing a graceful restart.
187 """
188 tgen = get_topogen()
189
190 logger.info(
191 "'{}': checking if a Grace-LSA was received from '{}'".format(
192 helper, restarting
193 )
194 )
195 test_func = partial(
196 topotest.router_json_cmp,
197 tgen.gears[helper],
198 "show ip ospf database opaque-link json",
199 {
200 "linkLocalOpaqueLsa": {
201 "areas": {
202 area: [
203 {
204 "advertisingRouter": restarting,
205 "opaqueType": "Grace-LSA",
206 }
207 ]
208 }
209 }
210 },
211 )
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)
214
215 assert result is None, assertmsg
216
217
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
229 # under load.
230 if initial_convergence == True:
231 tries = 240
232 else:
233 if restarting != None:
234 tries = 60
235 else:
236 tries = 1
237 router_compare_json_output(
238 rname, "show ip route ospf json", "show_ip_route.json", tries
239 )
240
241 # Check that all adjacencies are up and running (except when there's
242 # an OSPF instance that is shutting down).
243 if exiting == None:
244 tries = 240
245 router_compare_json_output(
246 rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json", tries
247 )
248
249 # Check the OSPF RIB and LSDB.
250 # In the restarting router, wait up to one minute for the LSDB to converge.
251 if exiting != rname:
252 if initial_convergence == True or restarting == rname:
253 tries = 240
254 else:
255 tries = 1
256 router_compare_json_output(
257 rname, "show ip ospf database json", "show_ip_ospf_database.json", tries
258 )
259 router_compare_json_output(
260 rname, "show ip ospf route json", "show_ip_ospf_route.json", tries
261 )
262
263
264 def ensure_gr_is_in_zebra(rname):
265 retry = True
266 retry_times = 10
267 tgen = get_topogen()
268
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 "'
272 )
273
274 if "Graceful Restart" not in out:
275 sleep(2)
276 retry_times -= 1
277 else:
278 retry = False
279
280 assertmsg = "%s does not appear to have Graceful Restart setup" % rname
281 assert not retry and retry_times > 0, assertmsg
282
283
284 #
285 # Test initial network convergence
286 #
287 def test_initial_convergence():
288 logger.info("Test: verify initial network convergence")
289 tgen = get_topogen()
290
291 # Skip if previous fatal error condition is raised
292 if tgen.routers_have_failure():
293 pytest.skip(tgen.errors)
294
295 check_routers(initial_convergence=True)
296
297
298 #
299 # Test rt1 performing a graceful restart
300 #
301 def test_gr_rt1():
302 logger.info("Test: verify rt1 performing a graceful restart")
303 tgen = get_topogen()
304
305 # Skip if previous fatal error condition is raised
306 if tgen.routers_have_failure():
307 pytest.skip(tgen.errors)
308
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")
314
315 start_router_daemons(tgen, "rt1", ["ospfd"])
316 check_routers(restarting="rt1")
317
318
319 #
320 # Test rt2 performing a graceful restart
321 #
322 def test_gr_rt2():
323 logger.info("Test: verify rt2 performing a graceful restart")
324 tgen = get_topogen()
325
326 # Skip if previous fatal error condition is raised
327 if tgen.routers_have_failure():
328 pytest.skip(tgen.errors)
329
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")
336
337 start_router_daemons(tgen, "rt2", ["ospfd"])
338 check_routers(restarting="rt2")
339
340
341 #
342 # Test rt3 performing a graceful restart
343 #
344 def test_gr_rt3():
345 logger.info("Test: verify rt3 performing a graceful restart")
346 tgen = get_topogen()
347
348 # Skip if previous fatal error condition is raised
349 if tgen.routers_have_failure():
350 pytest.skip(tgen.errors)
351
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")
359
360 start_router_daemons(tgen, "rt3", ["ospfd"])
361 check_routers(restarting="rt3")
362
363
364 #
365 # Test rt4 performing a graceful restart
366 #
367 def test_gr_rt4():
368 logger.info("Test: verify rt4 performing a graceful restart")
369 tgen = get_topogen()
370
371 # Skip if previous fatal error condition is raised
372 if tgen.routers_have_failure():
373 pytest.skip(tgen.errors)
374
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")
381
382 start_router_daemons(tgen, "rt4", ["ospfd"])
383 check_routers(restarting="rt4")
384
385
386 #
387 # Test rt5 performing a graceful restart
388 #
389 def test_gr_rt5():
390 logger.info("Test: verify rt5 performing a graceful restart")
391 tgen = get_topogen()
392
393 # Skip if previous fatal error condition is raised
394 if tgen.routers_have_failure():
395 pytest.skip(tgen.errors)
396
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")
402
403 start_router_daemons(tgen, "rt5", ["ospfd"])
404 check_routers(restarting="rt5")
405
406
407 #
408 # Test rt6 performing a graceful restart
409 #
410 def test_gr_rt6():
411 logger.info("Test: verify rt6 performing a graceful restart")
412 tgen = get_topogen()
413
414 # Skip if previous fatal error condition is raised
415 if tgen.routers_have_failure():
416 pytest.skip(tgen.errors)
417
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")
424
425 start_router_daemons(tgen, "rt6", ["ospfd"])
426 check_routers(restarting="rt6")
427
428
429 #
430 # Test rt7 performing a graceful restart
431 #
432 def test_gr_rt7():
433 logger.info("Test: verify rt7 performing a graceful restart")
434 tgen = get_topogen()
435
436 # Skip if previous fatal error condition is raised
437 if tgen.routers_have_failure():
438 pytest.skip(tgen.errors)
439
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")
445
446 start_router_daemons(tgen, "rt7", ["ospfd"])
447 check_routers(restarting="rt7")
448
449
450 # Memory leak test template
451 def test_memory_leak():
452 "Run the memory leak test and report results."
453 tgen = get_topogen()
454 if not tgen.is_memleak_enabled():
455 pytest.skip("Memory leak test/report is disabled")
456
457 tgen.report_memory_leaks()
458
459
460 if __name__ == "__main__":
461 args = ["-s"] + sys.argv[1:]
462 sys.exit(pytest.main(args))