]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/ospf6_topo1/test_ospf6_topo1.py
*: Add camelCase JSON keys in addition to PascalCase
[mirror_frr.git] / tests / topotests / ospf6_topo1 / test_ospf6_topo1.py
1 #!/usr/bin/env python
2
3 #
4 # test_ospf6_topo1.py
5 # Part of NetDEF Topology Tests
6 #
7 # Copyright (c) 2016 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_ospf6_topo1.py:
27
28 -----\
29 SW1 - Stub Net 1 SW2 - Stub Net 2 \
30 fc00:1:1:1::/64 fc00:2:2:2::/64 \
31 \___________________/ \___________________/ |
32 | | |
33 | | |
34 | ::1 | ::2 |
35 +---------+---------+ +---------+---------+ |
36 | R1 | | R2 | |
37 | FRRouting | | FRRouting | |
38 | Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | |
39 +---------+---------+ +---------+---------+ |
40 | ::1 | ::2 \
41 \______ ___________/ OSPFv3
42 \ / Area 0.0.0.0
43 \ / /
44 ~~~~~~~~~~~~~~~~~~ |
45 ~~ SW5 ~~ |
46 ~~ Switch ~~ |
47 ~~ fc00:A:A:A::/64 ~~ |
48 ~~~~~~~~~~~~~~~~~~ |
49 | /---- |
50 | ::3 | SW3 - Stub Net 3 |
51 +---------+---------+ /-+ fc00:3:3:3::/64 |
52 | R3 | / | /
53 | FRRouting +--/ \---- /
54 | Rtr-ID: 10.0.0.3 | ::3 ___________/
55 +---------+---------+ \
56 | ::3 \
57 | \
58 ~~~~~~~~~~~~~~~~~~ |
59 ~~ SW6 ~~ |
60 ~~ Switch ~~ |
61 ~~ fc00:B:B:B::/64 ~~ \
62 ~~~~~~~~~~~~~~~~~~ OSPFv3
63 | Area 0.0.0.1
64 | ::4 /
65 +---------+---------+ /---- |
66 | R4 | | SW4 - Stub Net 4 |
67 | FRRouting +------+ fc00:4:4:4::/64 |
68 | Rtr-ID: 10.0.0.4 | ::4 | /
69 +-------------------+ \---- /
70 -----/
71 """
72
73 import os
74 import re
75 import sys
76 import pytest
77
78 from functools import partial
79
80
81 # Save the Current Working Directory to find configuration files later.
82 CWD = os.path.dirname(os.path.realpath(__file__))
83 sys.path.append(os.path.join(CWD, "../"))
84
85 # pylint: disable=C0413
86 # Import topogen and topotest helpers
87 from lib import topotest
88 from lib.topogen import Topogen, TopoRouter, get_topogen
89 from lib.topolog import logger
90
91
92 pytestmark = [pytest.mark.ospfd]
93
94
95 def build_topo(tgen):
96 # Create 4 routers
97 for routern in range(1, 5):
98 tgen.add_router("r{}".format(routern))
99
100 #
101 # Wire up the switches and routers
102 # Note that we specify the link names so we match the config files
103 #
104
105 # Create a empty network for router 1
106 switch = tgen.add_switch("s1")
107 switch.add_link(tgen.gears["r1"], nodeif="r1-stubnet")
108
109 # Create a empty network for router 2
110 switch = tgen.add_switch("s2")
111 switch.add_link(tgen.gears["r2"], nodeif="r2-stubnet")
112
113 # Create a empty network for router 3
114 switch = tgen.add_switch("s3")
115 switch.add_link(tgen.gears["r3"], nodeif="r3-stubnet")
116
117 # Create a empty network for router 4
118 switch = tgen.add_switch("s4")
119 switch.add_link(tgen.gears["r4"], nodeif="r4-stubnet")
120
121 # Interconnect routers 1, 2, and 3
122 switch = tgen.add_switch("s5")
123 switch.add_link(tgen.gears["r1"], nodeif="r1-sw5")
124 switch.add_link(tgen.gears["r2"], nodeif="r2-sw5")
125 switch.add_link(tgen.gears["r3"], nodeif="r3-sw5")
126
127 # Interconnect routers 3 and 4
128 switch = tgen.add_switch("s6")
129 switch.add_link(tgen.gears["r3"], nodeif="r3-sw6")
130 switch.add_link(tgen.gears["r4"], nodeif="r4-sw6")
131
132
133 #####################################################
134 ##
135 ## Tests starting
136 ##
137 #####################################################
138
139
140 def setup_module(mod):
141 "Sets up the pytest environment"
142
143 tgen = Topogen(build_topo, mod.__name__)
144 tgen.start_topology()
145
146 logger.info("** %s: Setup Topology" % mod.__name__)
147 logger.info("******************************************")
148
149 # For debugging after starting net, but before starting FRR,
150 # uncomment the next line
151 # tgen.mininet_cli()
152
153 router_list = tgen.routers()
154 for rname, router in router_list.items():
155 router.load_config(
156 TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
157 )
158 router.load_config(
159 TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
160 )
161
162 # Initialize all routers.
163 tgen.start_router()
164
165 # For debugging after starting FRR daemons, uncomment the next line
166 # tgen.mininet_cli()
167
168
169 def teardown_module(mod):
170 "Teardown the pytest environment"
171 tgen = get_topogen()
172 tgen.stop_topology()
173
174
175 def test_wait_protocol_convergence():
176 "Wait for OSPFv3 to converge"
177 tgen = get_topogen()
178 if tgen.routers_have_failure():
179 pytest.skip(tgen.errors)
180
181 logger.info("waiting for protocols to converge")
182
183 def expect_neighbor_full(router, neighbor):
184 "Wait until OSPFv3 convergence."
185 logger.info("waiting OSPFv3 router '{}'".format(router))
186 test_func = partial(
187 topotest.router_json_cmp,
188 tgen.gears[router],
189 "show ipv6 ospf6 neighbor json",
190 {"neighbors": [{"neighborId": neighbor, "state": "Full"}]},
191 )
192 _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
193 assertmsg = '"{}" convergence failure'.format(router)
194 assert result is None, assertmsg
195
196 expect_neighbor_full("r1", "10.0.0.2")
197 expect_neighbor_full("r1", "10.0.0.3")
198
199 expect_neighbor_full("r2", "10.0.0.1")
200 expect_neighbor_full("r2", "10.0.0.3")
201
202 expect_neighbor_full("r3", "10.0.0.1")
203 expect_neighbor_full("r3", "10.0.0.2")
204 expect_neighbor_full("r3", "10.0.0.4")
205
206 expect_neighbor_full("r4", "10.0.0.3")
207
208
209 def compare_show_ipv6(rname, expected):
210 """
211 Calls 'show ipv6 route' for router `rname` and compare the obtained
212 result with the expected output.
213 """
214 tgen = get_topogen()
215
216 # Use the vtysh output, with some masking to make comparison easy
217 current = topotest.ip6_route_zebra(tgen.gears[rname])
218
219 # Use just the 'O'spf lines of the output
220 linearr = []
221 for line in current.splitlines():
222 if re.match("^O", line):
223 linearr.append(line)
224
225 current = "\n".join(linearr)
226
227 return topotest.difflines(
228 topotest.normalize_text(current),
229 topotest.normalize_text(expected),
230 title1="Current output",
231 title2="Expected output",
232 )
233
234
235 def test_ospfv3_routingTable():
236
237 tgen = get_topogen()
238 if tgen.routers_have_failure():
239 pytest.skip("skipped because of router(s) failure")
240
241 # For debugging, uncomment the next line
242 # tgen.mininet_cli()
243
244 # Verify OSPFv3 Routing Table
245 for router, rnode in tgen.routers().items():
246 logger.info('Waiting for router "%s" convergence', router)
247
248 # Load expected results from the command
249 reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router))
250 expected = open(reffile).read()
251
252 # Run test function until we get an result. Wait at most 60 seconds.
253 test_func = partial(compare_show_ipv6, router, expected)
254 result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
255 assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff)
256
257
258 def test_linux_ipv6_kernel_routingTable():
259
260 tgen = get_topogen()
261
262 if tgen.routers_have_failure():
263 pytest.skip("skipped because of router(s) failure")
264
265 # Verify Linux Kernel Routing Table
266 logger.info("Verifying Linux IPv6 Kernel Routing Table")
267
268 failures = 0
269
270 # Get a list of all current link-local addresses first as they change for
271 # each run and we need to translate them
272 linklocals = []
273 for i in range(1, 5):
274 linklocals += tgen.net["r{}".format(i)].get_ipv6_linklocal()
275
276 # Now compare the routing tables (after substituting link-local addresses)
277
278 for i in range(1, 5):
279 # Actual output from router
280 actual = tgen.gears["r{}".format(i)].run("ip -6 route").rstrip()
281 if "nhid" in actual:
282 refTableFile = os.path.join(CWD, "r{}/ip_6_address.nhg.ref".format(i))
283 else:
284 refTableFile = os.path.join(CWD, "r{}/ip_6_address.ref".format(i))
285
286 if os.path.isfile(refTableFile):
287 expected = open(refTableFile).read().rstrip()
288 # Fix newlines (make them all the same)
289 expected = ("\n".join(expected.splitlines())).splitlines(1)
290
291 # Mask out Link-Local mac addresses
292 for ll in linklocals:
293 actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0])
294 # Mask out protocol name or number
295 actual = re.sub(r"[ ]+proto [0-9a-z]+ +", " proto XXXX ", actual)
296 actual = re.sub(r"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual)
297 # Remove ff00::/8 routes (seen on some kernels - not from FRR)
298 actual = re.sub(r"ff00::/8.*", "", actual)
299
300 # Strip empty lines
301 actual = actual.lstrip()
302 actual = actual.rstrip()
303 actual = re.sub(r" +", " ", actual)
304
305 filtered_lines = []
306 for line in sorted(actual.splitlines()):
307 if line.startswith("fe80::/64 ") or line.startswith(
308 "unreachable fe80::/64 "
309 ):
310 continue
311 filtered_lines.append(line)
312 actual = "\n".join(filtered_lines).splitlines(1)
313
314 # Print Actual table
315 # logger.info("Router r%s table" % i)
316 # for line in actual:
317 # logger.info(line.rstrip())
318
319 # Generate Diff
320 diff = topotest.get_textdiff(
321 actual,
322 expected,
323 title1="actual OSPFv3 IPv6 routing table",
324 title2="expected OSPFv3 IPv6 routing table",
325 )
326
327 # Empty string if it matches, otherwise diff contains unified diff
328 if diff:
329 sys.stderr.write(
330 "r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n"
331 % (i, diff)
332 )
333 failures += 1
334 else:
335 logger.info("r%s ok" % i)
336
337 assert failures == 0, (
338 "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s"
339 % (i, diff)
340 )
341 else:
342 logger.error("r{} failed - no nhid ref file: {}".format(i, refTableFile))
343
344 assert False, (
345 "Linux Kernel IPv6 Routing Table verification failed for router r%s\n"
346 % (i)
347 )
348
349
350 def test_ospfv3_routingTable_write_multiplier():
351
352 tgen = get_topogen()
353 if tgen.routers_have_failure():
354 pytest.skip("skipped because of router(s) failure")
355
356 # For debugging, uncomment the next line
357 # tgen.mininet_cli()
358
359 # Modify R1 write muliplier and reset the interfaces
360 r1 = tgen.gears["r1"]
361
362 r1.vtysh_cmd("conf t\nrouter ospf6\n write-multiplier 100")
363 r1.vtysh_cmd("clear ipv6 ospf interface r1-stubnet")
364 r1.vtysh_cmd("clear ipv6 ospf interface r1-sw5")
365
366 # Verify OSPFv3 Routing Table
367 for router, rnode in tgen.routers().items():
368 logger.info('Waiting for router "%s" convergence', router)
369
370 # Load expected results from the command
371 reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router))
372 expected = open(reffile).read()
373
374 # Run test function until we get an result. Wait at most 60 seconds.
375 test_func = partial(compare_show_ipv6, router, expected)
376 result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
377 assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff)
378
379
380 def test_shutdown_check_stderr():
381
382 tgen = get_topogen()
383
384 if tgen.routers_have_failure():
385 pytest.skip("skipped because of router(s) failure")
386
387 if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
388 logger.info(
389 "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
390 )
391 pytest.skip("Skipping test for Stderr output")
392
393 net = tgen.net
394
395 logger.info("\n\n** Verifying unexpected STDERR output from daemons")
396 logger.info("******************************************")
397
398 for i in range(1, 5):
399 net["r%s" % i].stopRouter()
400 log = net["r%s" % i].getStdErr("ospf6d")
401 if log:
402 logger.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log))
403 log = net["r%s" % i].getStdErr("zebra")
404 if log:
405 logger.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log))
406
407
408 def test_shutdown_check_memleak():
409 "Run the memory leak test and report results."
410
411 if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None:
412 logger.info(
413 "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)"
414 )
415 pytest.skip("Skipping test for memory leaks")
416
417 tgen = get_topogen()
418
419 net = tgen.net
420
421 for i in range(1, 5):
422 net["r%s" % i].stopRouter()
423 net["r%s" % i].report_memory_leaks(
424 os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__)
425 )
426
427
428 if __name__ == "__main__":
429
430 # To suppress tracebacks, either use the following pytest call or
431 # add "--tb=no" to cli
432 # retval = pytest.main(["-s", "--tb=no"])
433
434 retval = pytest.main(["-s"])
435 sys.exit(retval)