]>
git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/ospf_topo1/test_ospf_topo1.py
2 # SPDX-License-Identifier: ISC
6 # Part of NetDEF Topology Tests
8 # Copyright (c) 2017 by
9 # Network Device Education Foundation, Inc. ("NetDEF")
13 test_ospf_topo1.py: Test the FRR OSPF routing daemon.
19 from functools
import partial
20 from time
import sleep
23 # Save the Current Working Directory to find configuration files.
24 CWD
= os
.path
.dirname(os
.path
.realpath(__file__
))
25 sys
.path
.append(os
.path
.join(CWD
, "../"))
27 # pylint: disable=C0413
28 # Import topogen and topotest helpers
29 from lib
import topotest
30 from lib
.topogen
import Topogen
, TopoRouter
, get_topogen
31 from lib
.topolog
import logger
33 # Required to instantiate the topology builder class.
35 pytestmark
= [pytest
.mark
.ospfd
]
42 for routern
in range(1, 5):
43 tgen
.add_router("r{}".format(routern
))
45 # Create a empty network for router 1
46 switch
= tgen
.add_switch("s1")
47 switch
.add_link(tgen
.gears
["r1"])
49 # Create a empty network for router 2
50 switch
= tgen
.add_switch("s2")
51 switch
.add_link(tgen
.gears
["r2"])
53 # Interconect router 1, 2 and 3
54 switch
= tgen
.add_switch("s3")
55 switch
.add_link(tgen
.gears
["r1"])
56 switch
.add_link(tgen
.gears
["r2"])
57 switch
.add_link(tgen
.gears
["r3"])
59 # Create empty netowrk for router3
60 switch
= tgen
.add_switch("s4")
61 switch
.add_link(tgen
.gears
["r3"])
63 # Interconect router 3 and 4
64 switch
= tgen
.add_switch("s5")
65 switch
.add_link(tgen
.gears
["r3"])
66 switch
.add_link(tgen
.gears
["r4"])
68 # Create a empty network for router 4
69 switch
= tgen
.add_switch("s6")
70 switch
.add_link(tgen
.gears
["r4"])
73 def setup_module(mod
):
74 "Sets up the pytest environment"
75 tgen
= Topogen(build_topo
, mod
.__name
__)
78 ospf6_config
= "ospf6d.conf"
80 router_list
= tgen
.routers()
81 for rname
, router
in router_list
.items():
83 TopoRouter
.RD_ZEBRA
, os
.path
.join(CWD
, "{}/zebra.conf".format(rname
))
86 TopoRouter
.RD_OSPF
, os
.path
.join(CWD
, "{}/ospfd.conf".format(rname
))
89 TopoRouter
.RD_OSPF6
, os
.path
.join(CWD
, "{}/{}".format(rname
, ospf6_config
))
92 # Initialize all routers.
96 def teardown_module(mod
):
97 "Teardown the pytest environment"
102 def test_wait_protocol_convergence():
103 "Wait for OSPFv2/OSPFv3 to converge"
105 if tgen
.routers_have_failure():
106 pytest
.skip(tgen
.errors
)
108 logger
.info("waiting for protocols to converge")
110 def expect_ospfv2_neighbor_full(router
, neighbor
):
111 "Wait until OSPFv2 convergence."
112 logger
.info("waiting OSPFv2 router '{}'".format(router
))
114 def run_command_and_expect():
116 Function that runs command and expect the following outcomes:
121 result
= tgen
.gears
[router
].vtysh_cmd(
122 "show ip ospf neighbor json", isjson
=True
126 result
, {"neighbors": {neighbor
: [{"converged": "Full"}]}}
134 result
, {"neighbors": {neighbor
: [{"converged": "Full"}]}}
140 return topotest
.json_cmp(
141 result
, {"neighbors": {neighbor
: [{"converged": "Full"}]}}
144 _
, result
= topotest
.run_and_expect(
145 run_command_and_expect
, None, count
=130, wait
=1
147 assertmsg
= '"{}" convergence failure'.format(router
)
148 assert result
is None, assertmsg
150 def expect_ospfv3_neighbor_full(router
, neighbor
):
151 "Wait until OSPFv3 convergence."
152 logger
.info("waiting OSPFv3 router '{}'".format(router
))
154 topotest
.router_json_cmp
,
156 "show ipv6 ospf6 neighbor json",
157 {"neighbors": [{"neighborId": neighbor
, "state": "Full"}]},
159 _
, result
= topotest
.run_and_expect(test_func
, None, count
=130, wait
=1)
160 assertmsg
= '"{}" convergence failure'.format(router
)
161 assert result
is None, assertmsg
163 # Wait for OSPFv2 convergence
164 expect_ospfv2_neighbor_full("r1", "10.0.255.2")
165 expect_ospfv2_neighbor_full("r1", "10.0.255.3")
166 expect_ospfv2_neighbor_full("r2", "10.0.255.1")
167 expect_ospfv2_neighbor_full("r2", "10.0.255.3")
168 expect_ospfv2_neighbor_full("r3", "10.0.255.1")
169 expect_ospfv2_neighbor_full("r3", "10.0.255.2")
170 expect_ospfv2_neighbor_full("r3", "10.0.255.4")
171 expect_ospfv2_neighbor_full("r4", "10.0.255.3")
173 # Wait for OSPFv3 convergence
174 expect_ospfv3_neighbor_full("r1", "10.0.255.2")
175 expect_ospfv3_neighbor_full("r1", "10.0.255.3")
176 expect_ospfv3_neighbor_full("r2", "10.0.255.1")
177 expect_ospfv3_neighbor_full("r2", "10.0.255.3")
178 expect_ospfv3_neighbor_full("r3", "10.0.255.1")
179 expect_ospfv3_neighbor_full("r3", "10.0.255.2")
180 expect_ospfv3_neighbor_full("r3", "10.0.255.4")
181 expect_ospfv3_neighbor_full("r4", "10.0.255.3")
184 def compare_show_ipv6_ospf6(rname
, expected
):
186 Calls 'show ipv6 ospf6 route' for router `rname` and compare the obtained
187 result with the expected output.
190 current
= tgen
.gears
[rname
].vtysh_cmd("show ipv6 ospf6 route")
192 # Remove the link addresses
193 current
= re
.sub(r
"fe80::[^ ]+", "fe80::xxxx:xxxx:xxxx:xxxx", current
)
194 expected
= re
.sub(r
"fe80::[^ ]+", "fe80::xxxx:xxxx:xxxx:xxxx", expected
)
197 current
= re
.sub(r
"\d+:\d{2}:\d{2}", "", current
)
198 expected
= re
.sub(r
"\d+:\d{2}:\d{2}", "", expected
)
200 return topotest
.difflines(
201 topotest
.normalize_text(current
),
202 topotest
.normalize_text(expected
),
203 title1
="Current output",
204 title2
="Expected output",
208 def test_ospf_convergence():
209 "Test OSPF daemon convergence"
211 if tgen
.routers_have_failure():
212 pytest
.skip("skipped because of router(s) failure")
214 for router
, rnode
in tgen
.routers().items():
215 logger
.info('Waiting for router "%s" convergence', router
)
217 # Load expected results from the command
218 reffile
= os
.path
.join(CWD
, "{}/ospfroute.txt".format(router
))
219 expected
= open(reffile
).read()
221 # Run test function until we get an result. Wait at most 80 seconds.
223 topotest
.router_output_cmp
, rnode
, "show ip ospf route", expected
225 result
, diff
= topotest
.run_and_expect(test_func
, "", count
=160, wait
=0.5)
226 assert result
, "OSPF did not converge on {}:\n{}".format(router
, diff
)
229 def test_ospf_kernel_route():
230 "Test OSPF kernel route installation"
232 if tgen
.routers_have_failure():
233 pytest
.skip("skipped because of router(s) failure")
235 rlist
= tgen
.routers().values()
237 logger
.info('Checking OSPF IPv4 kernel routes in "%s"', router
.name
)
239 routes
= topotest
.ip4_route(router
)
248 assertmsg
= 'OSPF IPv4 route mismatch in router "{}"'.format(router
.name
)
249 assert topotest
.json_cmp(routes
, expected
) is None, assertmsg
252 def test_ospf6_convergence():
253 "Test OSPF6 daemon convergence"
255 if tgen
.routers_have_failure():
256 pytest
.skip("skipped because of router(s) failure")
258 ospf6route_file
= "{}/ospf6route_ecmp.txt"
259 for rnum
in range(1, 5):
260 router
= "r{}".format(rnum
)
262 logger
.info('Waiting for router "%s" IPv6 OSPF convergence', router
)
264 # Load expected results from the command
265 reffile
= os
.path
.join(CWD
, ospf6route_file
.format(router
))
266 expected
= open(reffile
).read()
268 # Run test function until we get an result. Wait at most 60 seconds.
269 test_func
= partial(compare_show_ipv6_ospf6
, router
, expected
)
270 result
, diff
= topotest
.run_and_expect(test_func
, "", count
=25, wait
=3)
271 if (not result
) and (rnum
== 1):
272 # Didn't match the new ECMP version - try the old pre-ECMP format
273 ospf6route_file
= "{}/ospf6route.txt"
275 # Load expected results from the command
276 reffile
= os
.path
.join(CWD
, ospf6route_file
.format(router
))
277 expected
= open(reffile
).read()
279 test_func
= partial(compare_show_ipv6_ospf6
, router
, expected
)
280 result
, diff
= topotest
.run_and_expect(test_func
, "", count
=1, wait
=3)
282 # Didn't match the old version - switch back to new ECMP version
284 ospf6route_file
= "{}/ospf6route_ecmp.txt"
286 # Load expected results from the command
287 reffile
= os
.path
.join(CWD
, ospf6route_file
.format(router
))
288 expected
= open(reffile
).read()
290 test_func
= partial(compare_show_ipv6_ospf6
, router
, expected
)
291 result
, diff
= topotest
.run_and_expect(test_func
, "", count
=1, wait
=3)
293 assert result
, "OSPF6 did not converge on {}:\n{}".format(router
, diff
)
296 def test_ospf6_kernel_route():
297 "Test OSPF kernel route installation"
299 if tgen
.routers_have_failure():
300 pytest
.skip("skipped because of router(s) failure")
302 rlist
= tgen
.routers().values()
304 logger
.info('Checking OSPF IPv6 kernel routes in "%s"', router
.name
)
306 def _routes_in_fib6():
307 routes
= topotest
.ip6_route(router
)
309 "2001:db8:1::/64": {},
310 "2001:db8:2::/64": {},
311 "2001:db8:3::/64": {},
312 "2001:db8:100::/64": {},
313 "2001:db8:200::/64": {},
314 "2001:db8:300::/64": {},
316 logger
.info("Routes:")
318 logger
.info(topotest
.json_cmp(routes
, expected
))
320 return topotest
.json_cmp(routes
, expected
)
322 _
, result
= topotest
.run_and_expect(_routes_in_fib6
, None, count
=20, wait
=1)
324 assertmsg
= 'OSPF IPv6 route mismatch in router "{}"'.format(router
.name
)
325 assert result
is None, assertmsg
328 def test_ospf_json():
329 "Test 'show ip ospf json' output for coherency."
331 if tgen
.routers_have_failure():
332 pytest
.skip("skipped because of router(s) failure")
334 for rnum
in range(1, 5):
335 router
= tgen
.gears
["r{}".format(rnum
)]
336 logger
.info(router
.vtysh_cmd("show ip ospf database"))
337 logger
.info('Comparing router "%s" "show ip ospf json" output', router
.name
)
339 "routerId": "10.0.255.{}".format(rnum
),
340 "tosRoutesOnly": True,
341 "rfc2328Conform": True,
342 "spfScheduleDelayMsecs": 0,
343 "holdtimeMinMsecs": 50,
344 "holdtimeMaxMsecs": 5000,
345 "lsaMinIntervalMsecs": 5000,
346 "lsaMinArrivalMsecs": 1000,
347 "writeMultiplier": 20,
348 "refreshTimerMsecs": 10000,
349 "asbrRouter": "injectingExternalRoutingInformation",
350 "attachedAreaCounter": 1,
353 # Area specific additional checks
354 if router
.name
== "r1" or router
.name
== "r2" or router
.name
== "r3":
355 expected
["areas"]["0.0.0.0"] = {
356 "areaIfActiveCounter": 2,
357 "areaIfTotalCounter": 2,
358 "authentication": "authenticationNone",
361 "lsaNetworkNumber": 1,
364 "lsaOpaqueAreaNumber": 0,
365 "lsaOpaqueLinkNumber": 0,
366 "lsaRouterNumber": 3,
367 "lsaSummaryNumber": 2,
368 "nbrFullAdjacentCounter": 2,
370 if router
.name
== "r3" or router
.name
== "r4":
371 expected
["areas"]["0.0.0.1"] = {
372 "areaIfActiveCounter": 1,
373 "areaIfTotalCounter": 1,
374 "authentication": "authenticationNone",
376 "lsaNetworkNumber": 1,
379 "lsaOpaqueAreaNumber": 0,
380 "lsaOpaqueLinkNumber": 0,
381 "lsaRouterNumber": 2,
382 "lsaSummaryNumber": 4,
383 "nbrFullAdjacentCounter": 1,
385 # r4 has more interfaces for area 0.0.0.1
386 if router
.name
== "r4":
387 expected
["areas"]["0.0.0.1"].update(
389 "areaIfActiveCounter": 2,
390 "areaIfTotalCounter": 2,
394 # router 3 has an additional area
395 if router
.name
== "r3":
396 expected
["attachedAreaCounter"] = 2
398 output
= router
.vtysh_cmd("show ip ospf json", isjson
=True)
399 result
= topotest
.json_cmp(output
, expected
)
400 assert result
is None, '"{}" JSON output mismatches the expected result'.format(
405 def test_ospf_link_down():
406 "Test OSPF convergence after a link goes down"
408 if tgen
.routers_have_failure():
409 pytest
.skip("skipped because of router(s) failure")
411 # Simulate a network down event on router3 switch3 interface.
412 router3
= tgen
.gears
["r3"]
413 router3
.peer_link_enable("r3-eth0", False)
415 # Expect convergence on all routers
416 for router
, rnode
in tgen
.routers().items():
417 logger
.info('Waiting for router "%s" convergence after link failure', router
)
418 # Load expected results from the command
419 reffile
= os
.path
.join(CWD
, "{}/ospfroute_down.txt".format(router
))
420 expected
= open(reffile
).read()
422 # Run test function until we get an result. Wait at most 80 seconds.
424 topotest
.router_output_cmp
, rnode
, "show ip ospf route", expected
426 result
, diff
= topotest
.run_and_expect(test_func
, "", count
=140, wait
=0.5)
427 assert result
, "OSPF did not converge on {}:\n{}".format(router
, diff
)
430 def test_ospf_link_down_kernel_route():
431 "Test OSPF kernel route installation"
433 if tgen
.routers_have_failure():
434 pytest
.skip("skipped because of router(s) failure")
436 rlist
= tgen
.routers().values()
439 'Checking OSPF IPv4 kernel routes in "%s" after link down', router
.name
442 routes
= topotest
.ip4_route(router
)
451 if router
.name
== "r1" or router
.name
== "r2":
454 "10.0.10.0/24": None,
455 "172.16.0.0/24": None,
456 "172.16.1.0/24": None,
459 elif router
.name
== "r3" or router
.name
== "r4":
466 # Route '10.0.3.0' is no longer available for r4 since it is down.
467 if router
.name
== "r4":
473 assertmsg
= 'OSPF IPv4 route mismatch in router "{}" after link down'.format(
478 while not_found
and count
< 10:
479 not_found
= topotest
.json_cmp(routes
, expected
)
482 routes
= topotest
.ip4_route(router
)
487 assert not_found
is False, assertmsg
490 def test_ospf6_link_down():
491 "Test OSPF6 daemon convergence after link goes down"
493 if tgen
.routers_have_failure():
494 pytest
.skip("skipped because of router(s) failure")
496 for rnum
in range(1, 5):
497 router
= "r{}".format(rnum
)
500 'Waiting for router "%s" IPv6 OSPF convergence after link down', router
503 # Load expected results from the command
504 reffile
= os
.path
.join(CWD
, "{}/ospf6route_down.txt".format(router
))
505 expected
= open(reffile
).read()
507 # Run test function until we get an result. Wait at most 60 seconds.
508 test_func
= partial(compare_show_ipv6_ospf6
, router
, expected
)
509 result
, diff
= topotest
.run_and_expect(test_func
, "", count
=25, wait
=3)
510 assert result
, "OSPF6 did not converge on {}:\n{}".format(router
, diff
)
513 def test_ospf6_link_down_kernel_route():
514 "Test OSPF kernel route installation"
516 if tgen
.routers_have_failure():
517 pytest
.skip("skipped because of router(s) failure")
519 rlist
= tgen
.routers().values()
522 'Checking OSPF IPv6 kernel routes in "%s" after link down', router
.name
525 routes
= topotest
.ip6_route(router
)
527 "2001:db8:1::/64": {},
528 "2001:db8:2::/64": {},
529 "2001:db8:3::/64": {},
530 "2001:db8:100::/64": {},
531 "2001:db8:200::/64": {},
532 "2001:db8:300::/64": {},
534 if router
.name
== "r1" or router
.name
== "r2":
537 "2001:db8:100::/64": None,
538 "2001:db8:200::/64": None,
539 "2001:db8:300::/64": None,
542 elif router
.name
== "r3" or router
.name
== "r4":
545 "2001:db8:1::/64": None,
546 "2001:db8:2::/64": None,
549 # Route '2001:db8:3::/64' is no longer available for r4 since it is down.
550 if router
.name
== "r4":
553 "2001:db8:3::/64": None,
556 assertmsg
= 'OSPF IPv6 route mismatch in router "{}" after link down'.format(
561 while not_found
and count
< 10:
562 not_found
= topotest
.json_cmp(routes
, expected
)
565 routes
= topotest
.ip6_route(router
)
571 assert not_found
is False, assertmsg
574 def test_memory_leak():
575 "Run the memory leak test and report results."
577 if not tgen
.is_memleak_enabled():
578 pytest
.skip("Memory leak test/report is disabled")
580 tgen
.report_memory_leaks()
583 if __name__
== "__main__":
584 args
= ["-s"] + sys
.argv
[1:]
585 sys
.exit(pytest
.main(args
))