]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/ospf6-topo1/test_ospf6_topo1.py
tests: fix check for nhid in ipv6 table output
[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 from time import sleep
78
79 from functools import partial
80
81 from mininet.topo import Topo
82
83 # Save the Current Working Directory to find configuration files later.
84 CWD = os.path.dirname(os.path.realpath(__file__))
85 sys.path.append(os.path.join(CWD, '../'))
86
87 # pylint: disable=C0413
88 # Import topogen and topotest helpers
89 from lib import topotest
90 from lib.topogen import Topogen, TopoRouter, get_topogen
91 from lib.topolog import logger
92 import platform
93
94 #####################################################
95 ##
96 ## Network Topology Definition
97 ##
98 #####################################################
99
100 class NetworkTopo(Topo):
101 "OSPFv3 (IPv6) Test Topology 1"
102
103 def build(self, **_opts):
104 "Build function"
105
106 tgen = get_topogen(self)
107
108 # Create 4 routers
109 for routern in range(1, 5):
110 tgen.add_router('r{}'.format(routern))
111
112 #
113 # Wire up the switches and routers
114 # Note that we specify the link names so we match the config files
115 #
116
117 # Create a empty network for router 1
118 switch = tgen.add_switch('s1')
119 switch.add_link(tgen.gears['r1'], nodeif='r1-stubnet')
120
121 # Create a empty network for router 2
122 switch = tgen.add_switch('s2')
123 switch.add_link(tgen.gears['r2'], nodeif='r2-stubnet')
124
125 # Create a empty network for router 3
126 switch = tgen.add_switch('s3')
127 switch.add_link(tgen.gears['r3'], nodeif='r3-stubnet')
128
129 # Create a empty network for router 4
130 switch = tgen.add_switch('s4')
131 switch.add_link(tgen.gears['r4'], nodeif='r4-stubnet')
132
133 # Interconnect routers 1, 2, and 3
134 switch = tgen.add_switch('s5')
135 switch.add_link(tgen.gears['r1'], nodeif='r1-sw5')
136 switch.add_link(tgen.gears['r2'], nodeif='r2-sw5')
137 switch.add_link(tgen.gears['r3'], nodeif='r3-sw5')
138
139 # Interconnect routers 3 and 4
140 switch = tgen.add_switch('s6')
141 switch.add_link(tgen.gears['r3'], nodeif='r3-sw6')
142 switch.add_link(tgen.gears['r4'], nodeif='r4-sw6')
143
144
145 #####################################################
146 ##
147 ## Tests starting
148 ##
149 #####################################################
150
151 def setup_module(mod):
152 "Sets up the pytest environment"
153
154 tgen = Topogen(NetworkTopo, mod.__name__)
155 tgen.start_topology()
156
157 logger.info("** %s: Setup Topology" % mod.__name__)
158 logger.info("******************************************")
159
160 # For debugging after starting net, but before starting FRR,
161 # uncomment the next line
162 # tgen.mininet_cli()
163
164 router_list = tgen.routers()
165 for rname, router in router_list.iteritems():
166 router.load_config(
167 TopoRouter.RD_ZEBRA,
168 os.path.join(CWD, '{}/zebra.conf'.format(rname))
169 )
170 router.load_config(
171 TopoRouter.RD_OSPF6,
172 os.path.join(CWD, '{}/ospf6d.conf'.format(rname))
173 )
174
175 # Initialize all routers.
176 tgen.start_router()
177
178 # For debugging after starting FRR daemons, uncomment the next line
179 # tgen.mininet_cli()
180
181
182 def teardown_module(mod):
183 "Teardown the pytest environment"
184 tgen = get_topogen()
185 tgen.stop_topology()
186
187
188 def test_ospf6_converged():
189
190 tgen = get_topogen()
191
192 # Don't run this test if we have any failure.
193 if tgen.routers_have_failure():
194 pytest.skip(tgen.errors)
195
196 # For debugging, uncomment the next line
197 #tgen.mininet_cli()
198
199 # Wait for OSPF6 to converge (All Neighbors in either Full or TwoWay State)
200 logger.info("Waiting for OSPF6 convergence")
201
202 # Set up for regex
203 pat1 = re.compile('^[0-9]')
204 pat2 = re.compile('Full')
205
206 timeout = 60
207 while timeout > 0:
208 logger.info("Timeout in %s: " % timeout),
209 sys.stdout.flush()
210
211 # Look for any node not yet converged
212 for router, rnode in tgen.routers().iteritems():
213 resStr = rnode.vtysh_cmd('show ipv6 ospf neigh')
214
215 isConverged = False
216
217 for line in resStr.splitlines():
218 res1 = pat1.match(line)
219 if res1:
220 isConverged = True
221 res2 = pat2.search(line)
222
223 if res2 == None:
224 isConverged = False
225 break
226
227 if isConverged == False:
228 logger.info('Waiting for {}'.format(router))
229 sys.stdout.flush()
230 break
231
232 if isConverged:
233 logger.info('Done')
234 break
235 else:
236 sleep(5)
237 timeout -= 5
238
239 if timeout == 0:
240 # Bail out with error if a router fails to converge
241 ospfStatus = rnode.vtysh_cmd('show ipv6 ospf neigh')
242 assert False, "OSPFv6 did not converge:\n{}".format(ospfStatus)
243
244 logger.info("OSPFv3 converged.")
245
246 # For debugging, uncomment the next line
247 # tgen.mininet_cli()
248
249 # Make sure that all daemons are still running
250 if tgen.routers_have_failure():
251 assert tgen.errors == "", tgen.errors
252
253 def compare_show_ipv6(rname, expected):
254 """
255 Calls 'show ipv6 route' for router `rname` and compare the obtained
256 result with the expected output.
257 """
258 tgen = get_topogen()
259
260 # Use the vtysh output, with some masking to make comparison easy
261 current = topotest.ip6_route_zebra(tgen.gears[rname])
262
263 # Use just the 'O'spf lines of the output
264 linearr = []
265 for line in current.splitlines():
266 if re.match('^O', line):
267 linearr.append(line)
268
269 current = '\n'.join(linearr)
270
271 return topotest.difflines(topotest.normalize_text(current),
272 topotest.normalize_text(expected),
273 title1="Current output",
274 title2="Expected output")
275
276 def test_ospfv3_routingTable():
277
278 tgen = get_topogen()
279 if tgen.routers_have_failure():
280 pytest.skip('skipped because of router(s) failure')
281
282 # For debugging, uncomment the next line
283 # tgen.mininet_cli()
284
285 # Verify OSPFv3 Routing Table
286 for router, rnode in tgen.routers().iteritems():
287 logger.info('Waiting for router "%s" convergence', router)
288
289 # Load expected results from the command
290 reffile = os.path.join(CWD, '{}/show_ipv6_route.ref'.format(router))
291 expected = open(reffile).read()
292
293 # Run test function until we get an result. Wait at most 60 seconds.
294 test_func = partial(
295 compare_show_ipv6, router, expected)
296 result, diff = topotest.run_and_expect(test_func, '',
297 count=120, wait=0.5)
298 assert result, 'OSPFv3 did not converge on {}:\n{}'.format(router, diff)
299
300
301 def test_linux_ipv6_kernel_routingTable():
302
303 tgen = get_topogen()
304
305 if tgen.routers_have_failure():
306 pytest.skip('skipped because of router(s) failure')
307
308 # Verify Linux Kernel Routing Table
309 logger.info("Verifying Linux IPv6 Kernel Routing Table")
310
311 failures = 0
312
313 # Get a list of all current link-local addresses first as they change for
314 # each run and we need to translate them
315 linklocals = []
316 for i in range(1, 5):
317 linklocals += tgen.net['r{}'.format(i)].get_ipv6_linklocal()
318
319 # Now compare the routing tables (after substituting link-local addresses)
320
321 for i in range(1, 5):
322 # Actual output from router
323 actual = tgen.gears['r{}'.format(i)].run('ip -6 route').rstrip()
324 if "nhid" in actual:
325 refTableFile = os.path.join(CWD, 'r{}/ip_6_address.nhg.ref'.format(i))
326 else:
327 refTableFile = os.path.join(CWD, 'r{}/ip_6_address.ref'.format(i))
328
329 if os.path.isfile(refTableFile):
330 expected = open(refTableFile).read().rstrip()
331 # Fix newlines (make them all the same)
332 expected = ('\n'.join(expected.splitlines())).splitlines(1)
333
334 # Mask out Link-Local mac addresses
335 for ll in linklocals:
336 actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0])
337 # Mask out protocol name or number
338 actual = re.sub(r"[ ]+proto [0-9a-z]+ +", " proto XXXX ", actual)
339 actual = re.sub(r"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual)
340 # Remove ff00::/8 routes (seen on some kernels - not from FRR)
341 actual = re.sub(r'ff00::/8.*', '', actual)
342
343 # Strip empty lines
344 actual = actual.lstrip()
345 actual = actual.rstrip()
346 actual = re.sub(r' +', ' ', actual)
347
348 filtered_lines = []
349 for line in sorted(actual.splitlines()):
350 if line.startswith('fe80::/64 ') \
351 or line.startswith('unreachable fe80::/64 '):
352 continue
353 filtered_lines.append(line)
354 actual = '\n'.join(filtered_lines).splitlines(1)
355
356 # Print Actual table
357 # logger.info("Router r%s table" % i)
358 # for line in actual:
359 # logger.info(line.rstrip())
360
361 # Generate Diff
362 diff = topotest.get_textdiff(actual, expected,
363 title1="actual OSPFv3 IPv6 routing table",
364 title2="expected OSPFv3 IPv6 routing table")
365
366 # Empty string if it matches, otherwise diff contains unified diff
367 if diff:
368 sys.stderr.write('r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n' % (i, diff))
369 failures += 1
370 else:
371 logger.info("r%s ok" % i)
372
373 assert failures == 0, "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s" % (i, diff)
374
375
376 def test_shutdown_check_stderr():
377
378 tgen = get_topogen()
379
380 if tgen.routers_have_failure():
381 pytest.skip('skipped because of router(s) failure')
382
383 if os.environ.get('TOPOTESTS_CHECK_STDERR') is None:
384 logger.info("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n")
385 pytest.skip('Skipping test for Stderr output')
386
387 net = tgen.net
388
389 logger.info("\n\n** Verifying unexpected STDERR output from daemons")
390 logger.info("******************************************")
391
392 for i in range(1, 5):
393 net['r%s' % i].stopRouter()
394 log = net['r%s' % i].getStdErr('ospf6d')
395 if log:
396 logger.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log))
397 log = net['r%s' % i].getStdErr('zebra')
398 if log:
399 logger.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log))
400
401
402 def test_shutdown_check_memleak():
403 "Run the memory leak test and report results."
404
405 if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None:
406 logger.info("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)")
407 pytest.skip('Skipping test for memory leaks')
408
409 tgen = get_topogen()
410
411 net = tgen.net
412
413 for i in range(1, 5):
414 net['r%s' % i].stopRouter()
415 net['r%s' % i].report_memory_leaks(
416 os.environ.get('TOPOTESTS_CHECK_MEMLEAK'),
417 os.path.basename(__file__))
418
419
420 if __name__ == '__main__':
421
422 # To suppress tracebacks, either use the following pytest call or
423 # add "--tb=no" to cli
424 # retval = pytest.main(["-s", "--tb=no"])
425
426 retval = pytest.main(["-s"])
427 sys.exit(retval)