]>
Commit | Line | Data |
---|---|---|
9f3e0f64 MW |
1 | #!/usr/bin/env python |
2 | ||
3 | # | |
4 | # test_ripng_topo1.py | |
5 | # Part of NetDEF Topology Tests | |
6 | # | |
7 | # Copyright (c) 2017 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_ripng_topo1.py: Test of RIPng Topology | |
27 | ||
28 | """ | |
29 | ||
30 | import os | |
31 | import re | |
32 | import sys | |
9f3e0f64 MW |
33 | import pytest |
34 | import unicodedata | |
35 | from time import sleep | |
36 | ||
37 | from mininet.topo import Topo | |
38 | from mininet.net import Mininet | |
39 | from mininet.node import Node, OVSSwitch, Host | |
40 | from mininet.log import setLogLevel, info | |
41 | from mininet.cli import CLI | |
42 | from mininet.link import Intf | |
43 | ||
44 | from functools import partial | |
45 | ||
46 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
47 | from lib import topotest | |
48 | ||
49 | fatal_error = "" | |
50 | ||
51 | ||
52 | ##################################################### | |
53 | ## | |
54 | ## Network Topology Definition | |
55 | ## | |
56 | ##################################################### | |
57 | ||
787e7624 | 58 | |
9f3e0f64 MW |
59 | class NetworkTopo(Topo): |
60 | "RIPng Topology 1" | |
61 | ||
62 | def build(self, **_opts): | |
63 | ||
64 | # Setup Routers | |
65 | router = {} | |
66 | # | |
67 | # Setup Main Router | |
787e7624 | 68 | router[1] = topotest.addRouter(self, "r1") |
9f3e0f64 MW |
69 | # |
70 | # Setup RIPng Routers | |
71 | for i in range(2, 4): | |
787e7624 | 72 | router[i] = topotest.addRouter(self, "r%s" % i) |
9f3e0f64 MW |
73 | |
74 | # Setup Switches | |
75 | switch = {} | |
76 | # | |
77 | # On main router | |
78 | # First switch is for a dummy interface (for local network) | |
787e7624 | 79 | switch[1] = self.addSwitch("sw1", cls=topotest.LegacySwitch) |
80 | self.addLink(switch[1], router[1], intfName2="r1-eth0") | |
9f3e0f64 MW |
81 | # |
82 | # Switches for RIPng | |
83 | # switch 2 switch is for connection to RIP router | |
787e7624 | 84 | switch[2] = self.addSwitch("sw2", cls=topotest.LegacySwitch) |
85 | self.addLink(switch[2], router[1], intfName2="r1-eth1") | |
86 | self.addLink(switch[2], router[2], intfName2="r2-eth0") | |
9f3e0f64 | 87 | # switch 3 is between RIP routers |
787e7624 | 88 | switch[3] = self.addSwitch("sw3", cls=topotest.LegacySwitch) |
89 | self.addLink(switch[3], router[2], intfName2="r2-eth1") | |
90 | self.addLink(switch[3], router[3], intfName2="r3-eth1") | |
9f3e0f64 | 91 | # switch 4 is stub on remote RIP router |
787e7624 | 92 | switch[4] = self.addSwitch("sw4", cls=topotest.LegacySwitch) |
93 | self.addLink(switch[4], router[3], intfName2="r3-eth0") | |
9f3e0f64 | 94 | |
cfe9a587 DS |
95 | switch[5] = self.addSwitch("sw5", cls=topotest.LegacySwitch) |
96 | self.addLink(switch[5], router[1], intfName2="r1-eth2") | |
97 | switch[6] = self.addSwitch("sw6", cls=topotest.LegacySwitch) | |
98 | self.addLink(switch[6], router[1], intfName2="r1-eth3") | |
99 | ||
9f3e0f64 MW |
100 | |
101 | ##################################################### | |
102 | ## | |
103 | ## Tests starting | |
104 | ## | |
105 | ##################################################### | |
106 | ||
787e7624 | 107 | |
9f3e0f64 MW |
108 | def setup_module(module): |
109 | global topo, net | |
110 | ||
111 | print("\n\n** %s: Setup Topology" % module.__name__) | |
112 | print("******************************************\n") | |
113 | ||
114 | print("Cleanup old Mininet runs") | |
787e7624 | 115 | os.system("sudo mn -c > /dev/null 2>&1") |
9f3e0f64 MW |
116 | |
117 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
118 | topo = NetworkTopo() | |
119 | ||
120 | net = Mininet(controller=None, topo=topo) | |
121 | net.start() | |
122 | ||
123 | # Starting Routers | |
124 | # | |
125 | for i in range(1, 4): | |
787e7624 | 126 | net["r%s" % i].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir, i)) |
127 | net["r%s" % i].loadConf("ripngd", "%s/r%s/ripngd.conf" % (thisDir, i)) | |
128 | net["r%s" % i].startRouter() | |
9f3e0f64 | 129 | |
622c4996 | 130 | # For debugging after starting FRR daemons, uncomment the next line |
9f3e0f64 MW |
131 | # CLI(net) |
132 | ||
133 | ||
134 | def teardown_module(module): | |
135 | global net | |
136 | ||
137 | print("\n\n** %s: Shutdown Topology" % module.__name__) | |
138 | print("******************************************\n") | |
139 | ||
140 | # End - Shutdown network | |
141 | net.stop() | |
142 | ||
143 | ||
144 | def test_router_running(): | |
145 | global fatal_error | |
146 | global net | |
147 | ||
148 | # Skip if previous fatal error condition is raised | |
787e7624 | 149 | if fatal_error != "": |
9f3e0f64 MW |
150 | pytest.skip(fatal_error) |
151 | ||
622c4996 | 152 | print("\n\n** Check if FRR is running on each Router node") |
9f3e0f64 | 153 | print("******************************************\n") |
9f3e0f64 MW |
154 | |
155 | # Starting Routers | |
156 | for i in range(1, 4): | |
787e7624 | 157 | fatal_error = net["r%s" % i].checkRouterRunning() |
9f3e0f64 MW |
158 | assert fatal_error == "", fatal_error |
159 | ||
622c4996 | 160 | # For debugging after starting FRR daemons, uncomment the next line |
9f3e0f64 MW |
161 | # CLI(net) |
162 | ||
163 | ||
164 | def test_converge_protocols(): | |
165 | global fatal_error | |
166 | global net | |
167 | ||
168 | # Skip if previous fatal error condition is raised | |
787e7624 | 169 | if fatal_error != "": |
9f3e0f64 MW |
170 | pytest.skip(fatal_error) |
171 | ||
172 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
173 | ||
174 | print("\n\n** Waiting for protocols convergence") | |
175 | print("******************************************\n") | |
176 | ||
b01c46b9 DS |
177 | # Not really implemented yet - just sleep 11 secs for now |
178 | sleep(11) | |
9f3e0f64 | 179 | |
7e7fc73b MW |
180 | # Make sure that all daemons are running |
181 | for i in range(1, 4): | |
787e7624 | 182 | fatal_error = net["r%s" % i].checkRouterRunning() |
7e7fc73b MW |
183 | assert fatal_error == "", fatal_error |
184 | ||
622c4996 | 185 | # For debugging after starting FRR daemons, uncomment the next line |
787e7624 | 186 | # CLI(net) |
9f3e0f64 MW |
187 | |
188 | ||
189 | def test_ripng_status(): | |
190 | global fatal_error | |
191 | global net | |
192 | ||
193 | # Skip if previous fatal error condition is raised | |
787e7624 | 194 | if fatal_error != "": |
9f3e0f64 MW |
195 | pytest.skip(fatal_error) |
196 | ||
197 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
198 | ||
199 | # Verify RIP Status | |
b2764f90 | 200 | print("\n\n** Verifying RIPng status") |
9f3e0f64 MW |
201 | print("******************************************\n") |
202 | failures = 0 | |
203 | for i in range(1, 4): | |
787e7624 | 204 | refTableFile = "%s/r%s/ripng_status.ref" % (thisDir, i) |
9f3e0f64 MW |
205 | if os.path.isfile(refTableFile): |
206 | # Read expected result from file | |
207 | expected = open(refTableFile).read().rstrip() | |
208 | # Fix newlines (make them all the same) | |
787e7624 | 209 | expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) |
9f3e0f64 MW |
210 | |
211 | # Actual output from router | |
787e7624 | 212 | actual = ( |
213 | net["r%s" % i] | |
214 | .cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null') | |
215 | .rstrip() | |
216 | ) | |
9f3e0f64 MW |
217 | # Mask out Link-Local mac address portion. They are random... |
218 | actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual) | |
787e7624 | 219 | # Drop time in next due |
9f3e0f64 MW |
220 | actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual) |
221 | # Drop time in last update | |
222 | actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual) | |
223 | # Fix newlines (make them all the same) | |
787e7624 | 224 | actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) |
9f3e0f64 MW |
225 | |
226 | # Generate Diff | |
787e7624 | 227 | diff = topotest.get_textdiff( |
228 | actual, | |
229 | expected, | |
17070436 | 230 | title1="actual IPv6 RIPng status", |
787e7624 | 231 | title2="expected IPv6 RIPng status", |
232 | ) | |
9f3e0f64 MW |
233 | |
234 | # Empty string if it matches, otherwise diff contains unified diff | |
235 | if diff: | |
787e7624 | 236 | sys.stderr.write( |
237 | "r%s failed IPv6 RIPng status check:\n%s\n" % (i, diff) | |
238 | ) | |
9f3e0f64 MW |
239 | failures += 1 |
240 | else: | |
241 | print("r%s ok" % i) | |
242 | ||
787e7624 | 243 | assert failures == 0, "IPv6 RIPng status failed for router r%s:\n%s" % ( |
244 | i, | |
245 | diff, | |
246 | ) | |
9f3e0f64 | 247 | |
7e7fc73b MW |
248 | # Make sure that all daemons are running |
249 | for i in range(1, 4): | |
787e7624 | 250 | fatal_error = net["r%s" % i].checkRouterRunning() |
7e7fc73b MW |
251 | assert fatal_error == "", fatal_error |
252 | ||
622c4996 | 253 | # For debugging after starting FRR daemons, uncomment the next line |
9f3e0f64 MW |
254 | # CLI(net) |
255 | ||
256 | ||
257 | def test_ripng_routes(): | |
258 | global fatal_error | |
259 | global net | |
260 | ||
261 | # Skip if previous fatal error condition is raised | |
787e7624 | 262 | if fatal_error != "": |
9f3e0f64 MW |
263 | pytest.skip(fatal_error) |
264 | ||
265 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
266 | ||
267 | # Verify RIPng Status | |
b2764f90 | 268 | print("\n\n** Verifying RIPng routes") |
9f3e0f64 MW |
269 | print("******************************************\n") |
270 | failures = 0 | |
271 | for i in range(1, 4): | |
787e7624 | 272 | refTableFile = "%s/r%s/show_ipv6_ripng.ref" % (thisDir, i) |
9f3e0f64 MW |
273 | if os.path.isfile(refTableFile): |
274 | # Read expected result from file | |
275 | expected = open(refTableFile).read().rstrip() | |
276 | # Fix newlines (make them all the same) | |
787e7624 | 277 | expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) |
9f3e0f64 MW |
278 | |
279 | # Actual output from router | |
787e7624 | 280 | actual = ( |
281 | net["r%s" % i].cmd('vtysh -c "show ipv6 ripng" 2> /dev/null').rstrip() | |
282 | ) | |
9f3e0f64 MW |
283 | # Drop Time |
284 | actual = re.sub(r" [0-9][0-9]:[0-5][0-9]", " XX:XX", actual) | |
285 | # Mask out Link-Local mac address portion. They are random... | |
787e7624 | 286 | actual = re.sub( |
287 | r" fe80::[0-9a-f: ]+", " fe80::XXXX:XXXX:XXXX:XXXX ", actual | |
288 | ) | |
9f3e0f64 | 289 | # Remove trailing spaces on all lines |
787e7624 | 290 | actual = "\n".join([line.rstrip() for line in actual.splitlines()]) |
9f3e0f64 MW |
291 | |
292 | # Fix newlines (make them all the same) | |
787e7624 | 293 | actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) |
9f3e0f64 MW |
294 | |
295 | # Generate Diff | |
787e7624 | 296 | diff = topotest.get_textdiff( |
297 | actual, | |
298 | expected, | |
17070436 | 299 | title1="actual SHOW IPv6 RIPng", |
787e7624 | 300 | title2="expected SHOW IPv6 RIPng", |
301 | ) | |
9f3e0f64 MW |
302 | |
303 | # Empty string if it matches, otherwise diff contains unified diff | |
304 | if diff: | |
787e7624 | 305 | sys.stderr.write("r%s failed SHOW IPv6 RIPng check:\n%s\n" % (i, diff)) |
9f3e0f64 MW |
306 | failures += 1 |
307 | else: | |
308 | print("r%s ok" % i) | |
309 | ||
787e7624 | 310 | assert failures == 0, "SHOW IPv6 RIPng failed for router r%s:\n%s" % ( |
311 | i, | |
312 | diff, | |
313 | ) | |
9f3e0f64 | 314 | |
7e7fc73b MW |
315 | # Make sure that all daemons are running |
316 | for i in range(1, 4): | |
787e7624 | 317 | fatal_error = net["r%s" % i].checkRouterRunning() |
7e7fc73b MW |
318 | assert fatal_error == "", fatal_error |
319 | ||
622c4996 | 320 | # For debugging after starting FRR daemons, uncomment the next line |
9f3e0f64 MW |
321 | # CLI(net) |
322 | ||
323 | ||
324 | def test_zebra_ipv6_routingTable(): | |
325 | global fatal_error | |
326 | global net | |
327 | ||
328 | # Skip if previous fatal error condition is raised | |
787e7624 | 329 | if fatal_error != "": |
9f3e0f64 MW |
330 | pytest.skip(fatal_error) |
331 | ||
332 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
333 | ||
334 | # Verify OSPFv3 Routing Table | |
b2764f90 | 335 | print("\n\n** Verifying Zebra IPv6 Routing Table") |
9f3e0f64 MW |
336 | print("******************************************\n") |
337 | failures = 0 | |
338 | for i in range(1, 4): | |
787e7624 | 339 | refTableFile = "%s/r%s/show_ipv6_route.ref" % (thisDir, i) |
9f3e0f64 MW |
340 | if os.path.isfile(refTableFile): |
341 | # Read expected result from file | |
342 | expected = open(refTableFile).read().rstrip() | |
343 | # Fix newlines (make them all the same) | |
787e7624 | 344 | expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) |
9f3e0f64 MW |
345 | |
346 | # Actual output from router | |
787e7624 | 347 | actual = ( |
348 | net["r%s" % i] | |
349 | .cmd('vtysh -c "show ipv6 route" 2> /dev/null | grep "^R"') | |
350 | .rstrip() | |
351 | ) | |
9f3e0f64 MW |
352 | # Mask out Link-Local mac address portion. They are random... |
353 | actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual) | |
622c4996 | 354 | # Drop timers on end of line |
9f3e0f64 MW |
355 | actual = re.sub(r", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual) |
356 | # Fix newlines (make them all the same) | |
787e7624 | 357 | actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) |
9f3e0f64 MW |
358 | |
359 | # Generate Diff | |
787e7624 | 360 | diff = topotest.get_textdiff( |
361 | actual, | |
362 | expected, | |
17070436 | 363 | title1="actual Zebra IPv6 routing table", |
787e7624 | 364 | title2="expected Zebra IPv6 routing table", |
365 | ) | |
9f3e0f64 MW |
366 | |
367 | # Empty string if it matches, otherwise diff contains unified diff | |
368 | if diff: | |
787e7624 | 369 | sys.stderr.write( |
370 | "r%s failed Zebra IPv6 Routing Table Check:\n%s\n" % (i, diff) | |
371 | ) | |
9f3e0f64 MW |
372 | failures += 1 |
373 | else: | |
374 | print("r%s ok" % i) | |
375 | ||
9fa6ec14 | 376 | assert ( |
377 | failures == 0 | |
378 | ), "Zebra IPv6 Routing Table verification failed for router r%s:\n%s" % ( | |
379 | i, | |
380 | diff, | |
787e7624 | 381 | ) |
9f3e0f64 | 382 | |
7e7fc73b MW |
383 | # Make sure that all daemons are running |
384 | for i in range(1, 4): | |
787e7624 | 385 | fatal_error = net["r%s" % i].checkRouterRunning() |
7e7fc73b MW |
386 | assert fatal_error == "", fatal_error |
387 | ||
622c4996 | 388 | # For debugging after starting FRR daemons, uncomment the next line |
9f3e0f64 MW |
389 | # CLI(net) |
390 | ||
391 | ||
392 | def test_shutdown_check_stderr(): | |
393 | global fatal_error | |
394 | global net | |
395 | ||
396 | # Skip if previous fatal error condition is raised | |
787e7624 | 397 | if fatal_error != "": |
9f3e0f64 MW |
398 | pytest.skip(fatal_error) |
399 | ||
787e7624 | 400 | if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: |
401 | print( | |
402 | "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n" | |
403 | ) | |
404 | pytest.skip("Skipping test for Stderr output") | |
9f3e0f64 MW |
405 | |
406 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
407 | ||
b2764f90 | 408 | print("\n\n** Verifying unexpected STDERR output from daemons") |
9f3e0f64 MW |
409 | print("******************************************\n") |
410 | ||
787e7624 | 411 | net["r1"].stopRouter() |
9f3e0f64 | 412 | |
787e7624 | 413 | log = net["r1"].getStdErr("ripngd") |
8e957dbb MW |
414 | if log: |
415 | print("\nRIPngd StdErr Log:\n" + log) | |
787e7624 | 416 | log = net["r1"].getStdErr("zebra") |
8e957dbb MW |
417 | if log: |
418 | print("\nZebra StdErr Log:\n" + log) | |
9f3e0f64 MW |
419 | |
420 | ||
50c40bde MW |
421 | def test_shutdown_check_memleak(): |
422 | global fatal_error | |
423 | global net | |
424 | ||
425 | # Skip if previous fatal error condition is raised | |
787e7624 | 426 | if fatal_error != "": |
50c40bde MW |
427 | pytest.skip(fatal_error) |
428 | ||
787e7624 | 429 | if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None: |
430 | print( | |
431 | "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n" | |
432 | ) | |
433 | pytest.skip("Skipping test for memory leaks") | |
434 | ||
50c40bde MW |
435 | thisDir = os.path.dirname(os.path.realpath(__file__)) |
436 | ||
787e7624 | 437 | net["r1"].stopRouter() |
438 | net["r1"].report_memory_leaks( | |
439 | os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__) | |
440 | ) | |
50c40bde MW |
441 | |
442 | ||
787e7624 | 443 | if __name__ == "__main__": |
9f3e0f64 | 444 | |
787e7624 | 445 | setLogLevel("info") |
9f3e0f64 MW |
446 | # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli |
447 | # retval = pytest.main(["-s", "--tb=no"]) | |
448 | retval = pytest.main(["-s"]) | |
449 | sys.exit(retval) |