]>
Commit | Line | Data |
---|---|---|
4501fbca MW |
1 | #!/usr/bin/env python |
2 | ||
3 | # | |
4 | # test_all_protocol_startup.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_all_protocol_startup.py: Test of all protocols at same time | |
27 | ||
28 | """ | |
29 | ||
30 | import os | |
31 | import re | |
32 | import sys | |
4501fbca | 33 | import pytest |
6a57e103 | 34 | import glob |
4501fbca MW |
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 | ||
58 | class NetworkTopo(Topo): | |
59 | "All Protocol Startup Test" | |
60 | ||
61 | def build(self, **_opts): | |
62 | ||
63 | # Setup Routers | |
64 | router = {} | |
65 | # | |
66 | # Setup Main Router | |
67 | router[1] = topotest.addRouter(self, 'r1') | |
68 | # | |
69 | ||
70 | # Setup Switches | |
71 | switch = {} | |
72 | # | |
73 | for i in range(0, 10): | |
74 | switch[i] = self.addSwitch('sw%s' % i, cls=topotest.LegacySwitch) | |
75 | self.addLink(switch[i], router[1], intfName2='r1-eth%s' % i ) | |
76 | ||
77 | ||
78 | ##################################################### | |
79 | ## | |
80 | ## Tests starting | |
81 | ## | |
82 | ##################################################### | |
83 | ||
84 | def setup_module(module): | |
85 | global topo, net | |
86 | global fatal_error | |
87 | ||
88 | print("\n\n** %s: Setup Topology" % module.__name__) | |
89 | print("******************************************\n") | |
90 | ||
91 | print("Cleanup old Mininet runs") | |
92 | os.system('sudo mn -c > /dev/null 2>&1') | |
93 | os.system('sudo rm /tmp/r* > /dev/null 2>&1') | |
94 | ||
95 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
96 | topo = NetworkTopo() | |
97 | ||
98 | net = Mininet(controller=None, topo=topo) | |
99 | net.start() | |
100 | ||
101 | if net['r1'].get_routertype() != 'frr': | |
102 | fatal_error = "Test is only implemented for FRR" | |
103 | sys.stderr.write('\n\nTest is only implemented for FRR - Skipping\n\n') | |
104 | pytest.skip(fatal_error) | |
105 | ||
106 | # Starting Routers | |
107 | # | |
108 | # Main router | |
109 | for i in range(1, 2): | |
110 | net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i)) | |
111 | net['r%s' % i].loadConf('ripd', '%s/r%s/ripd.conf' % (thisDir, i)) | |
112 | net['r%s' % i].loadConf('ripngd', '%s/r%s/ripngd.conf' % (thisDir, i)) | |
113 | net['r%s' % i].loadConf('ospfd', '%s/r%s/ospfd.conf' % (thisDir, i)) | |
114 | net['r%s' % i].loadConf('ospf6d', '%s/r%s/ospf6d.conf' % (thisDir, i)) | |
115 | net['r%s' % i].loadConf('isisd', '%s/r%s/isisd.conf' % (thisDir, i)) | |
116 | net['r%s' % i].loadConf('bgpd', '%s/r%s/bgpd.conf' % (thisDir, i)) | |
117 | if net['r%s' % i].daemon_available('ldpd'): | |
118 | # Only test LDPd if it's installed and Kernel >= 4.5 | |
119 | net['r%s' % i].loadConf('ldpd', '%s/r%s/ldpd.conf' % (thisDir, i)) | |
120 | net['r%s' % i].startRouter() | |
121 | ||
122 | # For debugging after starting Quagga/FRR daemons, uncomment the next line | |
123 | # CLI(net) | |
124 | ||
125 | ||
126 | def teardown_module(module): | |
127 | global net | |
128 | ||
129 | print("\n\n** %s: Shutdown Topology" % module.__name__) | |
130 | print("******************************************\n") | |
131 | ||
132 | # End - Shutdown network | |
133 | net.stop() | |
134 | ||
135 | ||
136 | def test_router_running(): | |
137 | global fatal_error | |
138 | global net | |
139 | ||
140 | # Skip if previous fatal error condition is raised | |
141 | if (fatal_error != ""): | |
142 | pytest.skip(fatal_error) | |
143 | ||
144 | print("\n\n** Check if FRR/Quagga is running on each Router node") | |
145 | print("******************************************\n") | |
146 | sleep(5) | |
147 | ||
148 | # Starting Routers | |
149 | for i in range(1, 2): | |
150 | fatal_error = net['r%s' % i].checkRouterRunning() | |
151 | assert fatal_error == "", fatal_error | |
152 | ||
153 | # For debugging after starting FRR/Quagga daemons, uncomment the next line | |
154 | # CLI(net) | |
155 | ||
156 | ||
157 | def test_error_messages_vtysh(): | |
158 | global fatal_error | |
159 | global net | |
160 | ||
161 | # Skip if previous fatal error condition is raised | |
162 | if (fatal_error != ""): | |
163 | pytest.skip(fatal_error) | |
164 | ||
165 | print("\n\n** Check for error messages on VTYSH") | |
166 | print("******************************************\n") | |
167 | ||
168 | failures = 0 | |
169 | for i in range(1, 2): | |
170 | # | |
171 | # First checking Standard Output | |
172 | # | |
173 | ||
174 | # VTYSH output from router | |
175 | vtystdout = net['r%s' % i].cmd('vtysh -c "show version" 2> /dev/null').rstrip() | |
176 | ||
177 | # Fix newlines (make them all the same) | |
178 | vtystdout = ('\n'.join(vtystdout.splitlines()) + '\n').rstrip() | |
179 | # Drop everything starting with "FRRouting X.xx" message | |
180 | vtystdout = re.sub(r"FRRouting [0-9]+.*", "", vtystdout, flags=re.DOTALL) | |
181 | ||
555451fa | 182 | if (vtystdout == ''): |
4501fbca MW |
183 | print("r%s StdOut ok" % i) |
184 | ||
555451fa | 185 | assert vtystdout == '', "Vtysh StdOut Output check failed for router r%s" % i |
798fb593 | 186 | |
4501fbca MW |
187 | # |
188 | # Second checking Standard Error | |
189 | # | |
190 | ||
191 | # VTYSH StdErr output from router | |
192 | vtystderr = net['r%s' % i].cmd('vtysh -c "show version" > /dev/null').rstrip() | |
193 | ||
194 | # Fix newlines (make them all the same) | |
195 | vtystderr = ('\n'.join(vtystderr.splitlines()) + '\n').rstrip() | |
196 | # # Drop everything starting with "FRRouting X.xx" message | |
197 | # vtystderr = re.sub(r"FRRouting [0-9]+.*", "", vtystderr, flags=re.DOTALL) | |
198 | ||
555451fa | 199 | if (vtystderr == ''): |
4501fbca MW |
200 | print("r%s StdErr ok" % i) |
201 | ||
555451fa | 202 | assert vtystderr == '', "Vtysh StdErr Output check failed for router r%s" % i |
4501fbca | 203 | |
7e7fc73b MW |
204 | # Make sure that all daemons are running |
205 | for i in range(1, 2): | |
206 | fatal_error = net['r%s' % i].checkRouterRunning() | |
207 | assert fatal_error == "", fatal_error | |
208 | ||
4501fbca MW |
209 | # For debugging after starting FRR/Quagga daemons, uncomment the next line |
210 | # CLI(net) | |
211 | ||
212 | ||
213 | def test_error_messages_daemons(): | |
214 | global fatal_error | |
215 | global net | |
216 | ||
217 | # Skip if previous fatal error condition is raised | |
218 | if (fatal_error != ""): | |
219 | pytest.skip(fatal_error) | |
220 | ||
221 | print("\n\n** Check for error messages in daemons") | |
222 | print("******************************************\n") | |
223 | ||
224 | error_logs = "" | |
225 | ||
226 | for i in range(1, 2): | |
227 | log = net['r%s' % i].getStdErr('ripd') | |
228 | if log: | |
229 | error_logs += "r%s RIPd StdErr Output:\n" % i | |
230 | error_logs += log | |
231 | log = net['r%s' % i].getStdErr('ripngd') | |
232 | if log: | |
233 | error_logs += "r%s RIPngd StdErr Output:\n" % i | |
234 | error_logs += log | |
235 | log = net['r%s' % i].getStdErr('ospfd') | |
236 | if log: | |
237 | error_logs += "r%s OSPFd StdErr Output:\n" % i | |
238 | error_logs += log | |
239 | log = net['r%s' % i].getStdErr('ospf6d') | |
240 | if log: | |
241 | error_logs += "r%s OSPF6d StdErr Output:\n" % i | |
242 | error_logs += log | |
243 | log = net['r%s' % i].getStdErr('isisd') | |
244 | # ISIS shows debugging enabled status on StdErr | |
245 | # Remove these messages | |
246 | log = re.sub(r"^IS-IS .* debugging is on.*", "", log).rstrip() | |
247 | if log: | |
248 | error_logs += "r%s ISISd StdErr Output:\n" % i | |
249 | error_logs += log | |
250 | log = net['r%s' % i].getStdErr('bgpd') | |
251 | if log: | |
252 | error_logs += "r%s BGPd StdErr Output:\n" % i | |
253 | error_logs += log | |
254 | if (net['r%s' % i].daemon_available('ldpd')): | |
255 | log = net['r%s' % i].getStdErr('ldpd') | |
256 | if log: | |
257 | error_logs += "r%s LDPd StdErr Output:\n" % i | |
258 | error_logs += log | |
259 | log = net['r%s' % i].getStdErr('zebra') | |
260 | if log: | |
261 | error_logs += "r%s Zebra StdErr Output:\n" | |
262 | error_logs += log | |
263 | ||
264 | if error_logs: | |
265 | sys.stderr.write('Failed check for StdErr Output on daemons:\n%s\n' % error_logs) | |
266 | ||
08fa1af7 MW |
267 | # Ignoring the issue if told to ignore (ie not yet fixed) |
268 | if (error_logs != ""): | |
1026c19a | 269 | if (os.environ.get('bamboo_TOPOTESTS_ISSUE_349') == "IGNORE"): |
08fa1af7 MW |
270 | sys.stderr.write('Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349\n') |
271 | pytest.skip('Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349') | |
272 | ||
4501fbca MW |
273 | assert error_logs == "", "Daemons report errors to StdErr" |
274 | ||
275 | # For debugging after starting FRR/Quagga daemons, uncomment the next line | |
276 | # CLI(net) | |
277 | ||
278 | ||
279 | def test_converge_protocols(): | |
280 | global fatal_error | |
281 | global net | |
282 | ||
283 | # Skip if previous fatal error condition is raised | |
284 | if (fatal_error != ""): | |
285 | pytest.skip(fatal_error) | |
286 | ||
287 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
288 | ||
289 | print("\n\n** Waiting for protocols convergence") | |
290 | print("******************************************\n") | |
291 | ||
292 | # Not really implemented yet - just sleep 60 secs for now | |
293 | sleep(60) | |
294 | ||
7e7fc73b MW |
295 | # Make sure that all daemons are running |
296 | for i in range(1, 2): | |
297 | fatal_error = net['r%s' % i].checkRouterRunning() | |
298 | assert fatal_error == "", fatal_error | |
299 | ||
4501fbca MW |
300 | # For debugging after starting FRR/Quagga daemons, uncomment the next line |
301 | ## CLI(net) | |
302 | ||
303 | ||
304 | def test_rip_status(): | |
305 | global fatal_error | |
306 | global net | |
307 | ||
308 | # Skip if previous fatal error condition is raised | |
309 | if (fatal_error != ""): | |
310 | pytest.skip(fatal_error) | |
311 | ||
312 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
313 | ||
b2764f90 | 314 | print("\n\n** Verifying RIP status") |
4501fbca MW |
315 | print("******************************************\n") |
316 | failures = 0 | |
317 | for i in range(1, 2): | |
318 | refTableFile = '%s/r%s/rip_status.ref' % (thisDir, i) | |
319 | if os.path.isfile(refTableFile): | |
320 | # Read expected result from file | |
321 | expected = open(refTableFile).read().rstrip() | |
322 | # Fix newlines (make them all the same) | |
323 | expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) | |
324 | ||
325 | # Actual output from router | |
326 | actual = net['r%s' % i].cmd('vtysh -c "show ip rip status" 2> /dev/null').rstrip() | |
327 | # Drop time in next due | |
328 | actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual) | |
329 | # Drop time in last update | |
330 | actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual) | |
331 | # Fix newlines (make them all the same) | |
332 | actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) | |
333 | ||
334 | # Generate Diff | |
17070436 MW |
335 | diff = topotest.get_textdiff(actual, expected, |
336 | title1="actual IP RIP status", | |
337 | title2="expected IP RIP status") | |
4501fbca MW |
338 | |
339 | # Empty string if it matches, otherwise diff contains unified diff | |
340 | if diff: | |
341 | sys.stderr.write('r%s failed IP RIP status check:\n%s\n' % (i, diff)) | |
342 | failures += 1 | |
343 | else: | |
344 | print("r%s ok" % i) | |
345 | ||
346 | assert failures == 0, "IP RIP status failed for router r%s:\n%s" % (i, diff) | |
347 | ||
7e7fc73b MW |
348 | # Make sure that all daemons are running |
349 | for i in range(1, 2): | |
350 | fatal_error = net['r%s' % i].checkRouterRunning() | |
351 | assert fatal_error == "", fatal_error | |
352 | ||
4501fbca MW |
353 | # For debugging after starting FRR/Quagga daemons, uncomment the next line |
354 | # CLI(net) | |
355 | ||
356 | ||
357 | def test_ripng_status(): | |
358 | global fatal_error | |
359 | global net | |
360 | ||
361 | # Skip if previous fatal error condition is raised | |
362 | if (fatal_error != ""): | |
363 | pytest.skip(fatal_error) | |
364 | ||
365 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
366 | ||
b2764f90 | 367 | print("\n\n** Verifying RIPng status") |
4501fbca MW |
368 | print("******************************************\n") |
369 | failures = 0 | |
370 | for i in range(1, 2): | |
371 | refTableFile = '%s/r%s/ripng_status.ref' % (thisDir, i) | |
372 | if os.path.isfile(refTableFile): | |
373 | # Read expected result from file | |
374 | expected = open(refTableFile).read().rstrip() | |
375 | # Fix newlines (make them all the same) | |
376 | expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) | |
377 | ||
378 | # Actual output from router | |
379 | actual = net['r%s' % i].cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null').rstrip() | |
380 | # Mask out Link-Local mac address portion. They are random... | |
381 | actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual) | |
382 | # Drop time in next due | |
383 | actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual) | |
384 | # Drop time in last update | |
385 | actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual) | |
386 | # Fix newlines (make them all the same) | |
387 | actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) | |
388 | ||
389 | # Generate Diff | |
17070436 MW |
390 | diff = topotest.get_textdiff(actual, expected, |
391 | title1="actual IPv6 RIPng status", | |
392 | title2="expected IPv6 RIPng status") | |
4501fbca MW |
393 | |
394 | # Empty string if it matches, otherwise diff contains unified diff | |
395 | if diff: | |
396 | sys.stderr.write('r%s failed IPv6 RIPng status check:\n%s\n' % (i, diff)) | |
397 | failures += 1 | |
398 | else: | |
399 | print("r%s ok" % i) | |
400 | ||
401 | assert failures == 0, "IPv6 RIPng status failed for router r%s:\n%s" % (i, diff) | |
402 | ||
7e7fc73b MW |
403 | # Make sure that all daemons are running |
404 | for i in range(1, 2): | |
405 | fatal_error = net['r%s' % i].checkRouterRunning() | |
406 | assert fatal_error == "", fatal_error | |
407 | ||
4501fbca MW |
408 | # For debugging after starting FRR/Quagga daemons, uncomment the next line |
409 | # CLI(net) | |
410 | ||
411 | ||
412 | def test_ospfv2_interfaces(): | |
413 | global fatal_error | |
414 | global net | |
415 | ||
416 | # Skip if previous fatal error condition is raised | |
417 | if (fatal_error != ""): | |
418 | pytest.skip(fatal_error) | |
419 | ||
420 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
421 | ||
b2764f90 | 422 | print("\n\n** Verifying OSPFv2 interfaces") |
4501fbca MW |
423 | print("******************************************\n") |
424 | failures = 0 | |
425 | for i in range(1, 2): | |
426 | refTableFile = '%s/r%s/show_ip_ospf_interface.ref' % (thisDir, i) | |
427 | if os.path.isfile(refTableFile): | |
428 | # Read expected result from file | |
429 | expected = open(refTableFile).read().rstrip() | |
430 | # Fix newlines (make them all the same) | |
431 | expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) | |
432 | ||
433 | # Actual output from router | |
434 | actual = net['r%s' % i].cmd('vtysh -c "show ip ospf interface" 2> /dev/null').rstrip() | |
435 | # Mask out Bandwidth portion. They may change.. | |
436 | actual = re.sub(r"BW [0-9]+ Mbit", "BW XX Mbit", actual) | |
437 | # Drop time in next due | |
438 | actual = re.sub(r"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual) | |
985e6d50 MW |
439 | # Fix 'MTU mismatch detection: enabled' vs 'MTU mismatch detection:enabled' - accept both |
440 | actual = re.sub(r"MTU mismatch detection:([a-z]+.*)", r"MTU mismatch detection: \1", actual) | |
4501fbca MW |
441 | # Fix newlines (make them all the same) |
442 | actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) | |
443 | ||
444 | # Generate Diff | |
17070436 MW |
445 | diff = topotest.get_textdiff(actual, expected, |
446 | title1="actual SHOW IP OSPF INTERFACE", | |
447 | title2="expected SHOW IP OSPF INTERFACE") | |
4501fbca MW |
448 | |
449 | # Empty string if it matches, otherwise diff contains unified diff | |
450 | if diff: | |
451 | sys.stderr.write('r%s failed SHOW IP OSPF INTERFACE check:\n%s\n' % (i, diff)) | |
452 | failures += 1 | |
453 | else: | |
454 | print("r%s ok" % i) | |
455 | ||
08fa1af7 MW |
456 | # Ignoring the issue if told to ignore (ie not yet fixed) |
457 | if (failures != 0): | |
1026c19a | 458 | if (os.environ.get('bamboo_TOPOTESTS_ISSUE_348') == "IGNORE"): |
08fa1af7 MW |
459 | sys.stderr.write('Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348\n') |
460 | pytest.skip('Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348') | |
461 | ||
4501fbca MW |
462 | assert failures == 0, "SHOW IP OSPF INTERFACE failed for router r%s:\n%s" % (i, diff) |
463 | ||
7e7fc73b MW |
464 | # Make sure that all daemons are running |
465 | for i in range(1, 2): | |
466 | fatal_error = net['r%s' % i].checkRouterRunning() | |
467 | assert fatal_error == "", fatal_error | |
468 | ||
4501fbca MW |
469 | # For debugging after starting FRR/Quagga daemons, uncomment the next line |
470 | # CLI(net) | |
471 | ||
472 | ||
473 | def test_isis_interfaces(): | |
474 | global fatal_error | |
475 | global net | |
476 | ||
477 | # Skip if previous fatal error condition is raised | |
478 | if (fatal_error != ""): | |
479 | pytest.skip(fatal_error) | |
480 | ||
481 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
482 | ||
b2764f90 | 483 | print("\n\n** Verifying ISIS interfaces") |
4501fbca MW |
484 | print("******************************************\n") |
485 | failures = 0 | |
486 | for i in range(1, 2): | |
487 | refTableFile = '%s/r%s/show_isis_interface_detail.ref' % (thisDir, i) | |
488 | if os.path.isfile(refTableFile): | |
489 | # Read expected result from file | |
490 | expected = open(refTableFile).read().rstrip() | |
491 | # Fix newlines (make them all the same) | |
492 | expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) | |
493 | ||
494 | # Actual output from router | |
495 | actual = net['r%s' % i].cmd('vtysh -c "show isis interface detail" 2> /dev/null').rstrip() | |
496 | # Mask out Link-Local mac address portion. They are random... | |
497 | actual = re.sub(r"fe80::[0-9a-f:]+", "fe80::XXXX:XXXX:XXXX:XXXX", actual) | |
498 | # Mask out SNPA mac address portion. They are random... | |
499 | actual = re.sub(r"SNPA: [0-9a-f\.]+", "SNPA: XXXX.XXXX.XXXX", actual) | |
6ae351e8 MW |
500 | # Mask out Circuit ID number |
501 | actual = re.sub(r"Circuit Id: 0x[0-9]+", "Circuit Id: 0xXX", actual) | |
4501fbca MW |
502 | # Fix newlines (make them all the same) |
503 | actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) | |
504 | ||
505 | # Generate Diff | |
17070436 MW |
506 | diff = topotest.get_textdiff(actual, expected, |
507 | title1="actual SHOW ISIS INTERFACE DETAIL", | |
508 | title2="expected SHOW ISIS OSPF6 INTERFACE DETAIL") | |
4501fbca MW |
509 | |
510 | # Empty string if it matches, otherwise diff contains unified diff | |
511 | if diff: | |
512 | sys.stderr.write('r%s failed SHOW ISIS INTERFACE DETAIL check:\n%s\n' % (i, diff)) | |
513 | failures += 1 | |
514 | else: | |
515 | print("r%s ok" % i) | |
516 | ||
517 | assert failures == 0, "SHOW ISIS INTERFACE DETAIL failed for router r%s:\n%s" % (i, diff) | |
518 | ||
7e7fc73b MW |
519 | # Make sure that all daemons are running |
520 | for i in range(1, 2): | |
521 | fatal_error = net['r%s' % i].checkRouterRunning() | |
522 | assert fatal_error == "", fatal_error | |
523 | ||
4501fbca MW |
524 | # For debugging after starting FRR/Quagga daemons, uncomment the next line |
525 | # CLI(net) | |
526 | ||
527 | ||
528 | def test_bgp_summary(): | |
529 | global fatal_error | |
530 | global net | |
531 | ||
532 | # Skip if previous fatal error condition is raised | |
533 | if (fatal_error != ""): | |
534 | pytest.skip(fatal_error) | |
535 | ||
536 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
537 | ||
b2764f90 | 538 | print("\n\n** Verifying BGP Summary") |
4501fbca MW |
539 | print("******************************************\n") |
540 | failures = 0 | |
541 | for i in range(1, 2): | |
08fa1af7 | 542 | refTableFile = '%s/r%s/show_ip_bgp_summary.ref' % (thisDir, i) |
4501fbca MW |
543 | if os.path.isfile(refTableFile): |
544 | # Read expected result from file | |
545 | expected = open(refTableFile).read().rstrip() | |
546 | # Fix newlines (make them all the same) | |
547 | expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) | |
548 | ||
549 | # Actual output from router | |
08fa1af7 | 550 | actual = net['r%s' % i].cmd('vtysh -c "show ip bgp summary" 2> /dev/null').rstrip() |
4501fbca MW |
551 | # Mask out "using XXiXX bytes" portion. They are random... |
552 | actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual) | |
553 | # Mask out "using XiXXX KiB" portion. They are random... | |
554 | actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual) | |
08fa1af7 MW |
555 | # |
556 | # Remove extra summaries which exist with newer versions | |
557 | # | |
558 | # Remove summary lines (changed recently) | |
559 | actual = re.sub(r'Total number.*', '', actual) | |
560 | actual = re.sub(r'Displayed.*', '', actual) | |
561 | # Remove IPv4 Unicast Summary (Title only) | |
562 | actual = re.sub(r'IPv4 Unicast Summary:', '', actual) | |
563 | # Remove IPv4 Multicast Summary (all of it) | |
564 | actual = re.sub(r'IPv4 Multicast Summary:', '', actual) | |
565 | actual = re.sub(r'No IPv4 Multicast neighbor is configured', '', actual) | |
566 | # Remove IPv4 VPN Summary (all of it) | |
567 | actual = re.sub(r'IPv4 VPN Summary:', '', actual) | |
568 | actual = re.sub(r'No IPv4 VPN neighbor is configured', '', actual) | |
569 | # Remove IPv4 Encap Summary (all of it) | |
570 | actual = re.sub(r'IPv4 Encap Summary:', '', actual) | |
571 | actual = re.sub(r'No IPv4 Encap neighbor is configured', '', actual) | |
572 | # Remove Unknown Summary (all of it) | |
573 | actual = re.sub(r'Unknown Summary:', '', actual) | |
574 | actual = re.sub(r'No Unknown neighbor is configured', '', actual) | |
8b2e59e9 DS |
575 | |
576 | actual = re.sub(r'IPv4 labeled-unicast Summary:', '', actual) | |
577 | actual = re.sub(r'No IPv4 labeled-unicast neighbor is configured', '', actual) | |
578 | ||
08fa1af7 MW |
579 | # Strip empty lines |
580 | actual = actual.lstrip() | |
581 | actual = actual.rstrip() | |
582 | # | |
4501fbca MW |
583 | # Fix newlines (make them all the same) |
584 | actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) | |
585 | ||
586 | # Generate Diff | |
17070436 MW |
587 | diff = topotest.get_textdiff(actual, expected, |
588 | title1="actual SHOW IP BGP SUMMARY", | |
589 | title2="expected SHOW IP BGP SUMMARY") | |
4501fbca MW |
590 | |
591 | # Empty string if it matches, otherwise diff contains unified diff | |
592 | if diff: | |
08fa1af7 | 593 | sys.stderr.write('r%s failed SHOW IP BGP SUMMARY check:\n%s\n' % (i, diff)) |
4501fbca MW |
594 | failures += 1 |
595 | else: | |
596 | print("r%s ok" % i) | |
597 | ||
08fa1af7 | 598 | assert failures == 0, "SHOW IP BGP SUMMARY failed for router r%s:\n%s" % (i, diff) |
4501fbca | 599 | |
7e7fc73b MW |
600 | # Make sure that all daemons are running |
601 | for i in range(1, 2): | |
602 | fatal_error = net['r%s' % i].checkRouterRunning() | |
603 | assert fatal_error == "", fatal_error | |
604 | ||
4501fbca MW |
605 | # For debugging after starting FRR/Quagga daemons, uncomment the next line |
606 | # CLI(net) | |
607 | ||
608 | ||
609 | def test_bgp_ipv6_summary(): | |
610 | global fatal_error | |
611 | global net | |
612 | ||
613 | # Skip if previous fatal error condition is raised | |
614 | if (fatal_error != ""): | |
615 | pytest.skip(fatal_error) | |
616 | ||
617 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
618 | ||
b2764f90 | 619 | print("\n\n** Verifying BGP IPv6 Summary") |
4501fbca MW |
620 | print("******************************************\n") |
621 | failures = 0 | |
622 | for i in range(1, 2): | |
623 | refTableFile = '%s/r%s/show_bgp_ipv6_summary.ref' % (thisDir, i) | |
624 | if os.path.isfile(refTableFile): | |
625 | # Read expected result from file | |
626 | expected = open(refTableFile).read().rstrip() | |
627 | # Fix newlines (make them all the same) | |
628 | expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) | |
629 | ||
630 | # Actual output from router | |
631 | actual = net['r%s' % i].cmd('vtysh -c "show bgp ipv6 summary" 2> /dev/null').rstrip() | |
632 | # Mask out "using XXiXX bytes" portion. They are random... | |
633 | actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual) | |
634 | # Mask out "using XiXXX KiB" portion. They are random... | |
635 | actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual) | |
08fa1af7 MW |
636 | # |
637 | # Remove extra summaries which exist with newer versions | |
638 | # | |
639 | # Remove summary lines (changed recently) | |
640 | actual = re.sub(r'Total number.*', '', actual) | |
641 | actual = re.sub(r'Displayed.*', '', actual) | |
642 | # Remove IPv4 Unicast Summary (Title only) | |
643 | actual = re.sub(r'IPv6 Unicast Summary:', '', actual) | |
644 | # Remove IPv4 Multicast Summary (all of it) | |
645 | actual = re.sub(r'IPv6 Multicast Summary:', '', actual) | |
646 | actual = re.sub(r'No IPv6 Multicast neighbor is configured', '', actual) | |
647 | # Remove IPv4 VPN Summary (all of it) | |
648 | actual = re.sub(r'IPv6 VPN Summary:', '', actual) | |
649 | actual = re.sub(r'No IPv6 VPN neighbor is configured', '', actual) | |
650 | # Remove IPv4 Encap Summary (all of it) | |
651 | actual = re.sub(r'IPv6 Encap Summary:', '', actual) | |
652 | actual = re.sub(r'No IPv6 Encap neighbor is configured', '', actual) | |
653 | # Remove Unknown Summary (all of it) | |
654 | actual = re.sub(r'Unknown Summary:', '', actual) | |
655 | actual = re.sub(r'No Unknown neighbor is configured', '', actual) | |
8b2e59e9 DS |
656 | |
657 | # Remove Labeled Unicast Summary (all of it) | |
658 | actual = re.sub(r'IPv6 labeled-unicast Summary:', '', actual) | |
659 | actual = re.sub(r'No IPv6 labeled-unicast neighbor is configured', '', actual) | |
660 | ||
08fa1af7 MW |
661 | # Strip empty lines |
662 | actual = actual.lstrip() | |
663 | actual = actual.rstrip() | |
664 | # | |
4501fbca MW |
665 | # Fix newlines (make them all the same) |
666 | actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) | |
667 | ||
668 | # Generate Diff | |
17070436 MW |
669 | diff = topotest.get_textdiff(actual, expected, |
670 | title1="actual SHOW BGP IPv6 SUMMARY", | |
671 | title2="expected SHOW BGP IPv6 SUMMARY") | |
4501fbca MW |
672 | |
673 | # Empty string if it matches, otherwise diff contains unified diff | |
674 | if diff: | |
675 | sys.stderr.write('r%s failed SHOW BGP IPv6 SUMMARY check:\n%s\n' % (i, diff)) | |
676 | failures += 1 | |
677 | else: | |
678 | print("r%s ok" % i) | |
679 | ||
680 | assert failures == 0, "SHOW BGP IPv6 SUMMARY failed for router r%s:\n%s" % (i, diff) | |
681 | ||
7e7fc73b MW |
682 | # Make sure that all daemons are running |
683 | for i in range(1, 2): | |
684 | fatal_error = net['r%s' % i].checkRouterRunning() | |
685 | assert fatal_error == "", fatal_error | |
686 | ||
4501fbca MW |
687 | # For debugging after starting FRR/Quagga daemons, uncomment the next line |
688 | # CLI(net) | |
689 | ||
690 | ||
691 | def test_bgp_ipv4(): | |
692 | global fatal_error | |
693 | global net | |
694 | ||
695 | # Skip if previous fatal error condition is raised | |
696 | if (fatal_error != ""): | |
697 | pytest.skip(fatal_error) | |
698 | ||
699 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
700 | ||
b2764f90 | 701 | print("\n\n** Verifying BGP IPv4") |
4501fbca | 702 | print("******************************************\n") |
6a57e103 | 703 | diffresult = {} |
4501fbca | 704 | for i in range(1, 2): |
6a57e103 PZ |
705 | success = 0 |
706 | for refTableFile in (glob.glob( | |
707 | '%s/r%s/show_bgp_ipv4*.ref' % (thisDir, i))): | |
708 | if os.path.isfile(refTableFile): | |
709 | # Read expected result from file | |
710 | expected = open(refTableFile).read().rstrip() | |
711 | # Fix newlines (make them all the same) | |
712 | expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) | |
713 | ||
714 | # Actual output from router | |
715 | actual = net['r%s' % i].cmd('vtysh -c "show bgp ipv4" 2> /dev/null').rstrip() | |
716 | # Remove summary line (changed recently) | |
717 | actual = re.sub(r'Total number.*', '', actual) | |
718 | actual = re.sub(r'Displayed.*', '', actual) | |
719 | actual = actual.rstrip() | |
720 | # Fix newlines (make them all the same) | |
721 | actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) | |
722 | ||
723 | # Generate Diff | |
724 | diff = topotest.get_textdiff(actual, expected, | |
725 | title1="actual SHOW BGP IPv4", | |
726 | title2="expected SHOW BGP IPv4") | |
727 | ||
728 | # Empty string if it matches, otherwise diff contains unified diff | |
729 | if diff: | |
730 | diffresult[refTableFile] = diff | |
731 | else: | |
732 | success = 1 | |
733 | print("template %s matched: r%s ok" % (refTableFile, i)) | |
734 | break | |
735 | ||
736 | if not success: | |
737 | resultstr = 'No template matched.\n' | |
738 | for f in diffresult.iterkeys(): | |
739 | resultstr += ( | |
740 | 'template %s: r%s failed SHOW BGP IPv4 check:\n%s\n' | |
741 | % (f, i, diffresult[f])) | |
742 | raise AssertionError( | |
743 | "SHOW BGP IPv4 failed for router r%s:\n%s" % (i, resultstr)) | |
4501fbca | 744 | |
7e7fc73b MW |
745 | # Make sure that all daemons are running |
746 | for i in range(1, 2): | |
747 | fatal_error = net['r%s' % i].checkRouterRunning() | |
748 | assert fatal_error == "", fatal_error | |
749 | ||
4501fbca MW |
750 | # For debugging after starting FRR/Quagga daemons, uncomment the next line |
751 | # CLI(net) | |
752 | ||
753 | ||
754 | def test_bgp_ipv6(): | |
755 | global fatal_error | |
756 | global net | |
757 | ||
758 | # Skip if previous fatal error condition is raised | |
759 | if (fatal_error != ""): | |
760 | pytest.skip(fatal_error) | |
761 | ||
762 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
763 | ||
b2764f90 | 764 | print("\n\n** Verifying BGP IPv6") |
4501fbca | 765 | print("******************************************\n") |
6a57e103 | 766 | diffresult = {} |
4501fbca | 767 | for i in range(1, 2): |
6a57e103 PZ |
768 | success = 0 |
769 | for refTableFile in (glob.glob( | |
770 | '%s/r%s/show_bgp_ipv6*.ref' % (thisDir, i))): | |
771 | if os.path.isfile(refTableFile): | |
772 | # Read expected result from file | |
773 | expected = open(refTableFile).read().rstrip() | |
774 | # Fix newlines (make them all the same) | |
775 | expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) | |
776 | ||
777 | # Actual output from router | |
778 | actual = net['r%s' % i].cmd('vtysh -c "show bgp ipv6" 2> /dev/null').rstrip() | |
779 | # Remove summary line (changed recently) | |
780 | actual = re.sub(r'Total number.*', '', actual) | |
781 | actual = re.sub(r'Displayed.*', '', actual) | |
782 | actual = actual.rstrip() | |
783 | # Fix newlines (make them all the same) | |
784 | actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) | |
785 | ||
786 | # Generate Diff | |
787 | diff = topotest.get_textdiff(actual, expected, | |
788 | title1="actual SHOW BGP IPv6", | |
789 | title2="expected SHOW BGP IPv6") | |
790 | ||
791 | # Empty string if it matches, otherwise diff contains unified diff | |
792 | if diff: | |
793 | diffresult[refTableFile] = diff | |
794 | else: | |
795 | success = 1 | |
796 | print("template %s matched: r%s ok" % (refTableFile, i)) | |
797 | ||
798 | if not success: | |
799 | resultstr = 'No template matched.\n' | |
800 | for f in diffresult.iterkeys(): | |
801 | resultstr += ( | |
802 | 'template %s: r%s failed SHOW BGP IPv6 check:\n%s\n' | |
803 | % (f, i, diffresult[f])) | |
804 | raise AssertionError( | |
805 | "SHOW BGP IPv6 failed for router r%s:\n%s" % (i, resultstr)) | |
4501fbca | 806 | |
7e7fc73b MW |
807 | # Make sure that all daemons are running |
808 | for i in range(1, 2): | |
809 | fatal_error = net['r%s' % i].checkRouterRunning() | |
810 | assert fatal_error == "", fatal_error | |
811 | ||
4501fbca MW |
812 | # For debugging after starting FRR/Quagga daemons, uncomment the next line |
813 | # CLI(net) | |
814 | ||
815 | ||
816 | ||
817 | def test_mpls_interfaces(): | |
818 | global fatal_error | |
819 | global net | |
820 | ||
821 | # Skip if previous fatal error condition is raised | |
822 | if (fatal_error != ""): | |
823 | pytest.skip(fatal_error) | |
824 | ||
825 | # Skip if no LDP installed or old kernel | |
826 | if (net['r1'].daemon_available('ldpd') == False): | |
827 | pytest.skip("No MPLS or kernel < 4.5") | |
828 | ||
829 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
830 | ||
b2764f90 | 831 | print("\n\n** Verifying MPLS Interfaces") |
4501fbca MW |
832 | print("******************************************\n") |
833 | failures = 0 | |
834 | for i in range(1, 2): | |
835 | refTableFile = '%s/r%s/show_mpls_ldp_interface.ref' % (thisDir, i) | |
836 | if os.path.isfile(refTableFile): | |
837 | # Read expected result from file | |
838 | expected = open(refTableFile).read().rstrip() | |
839 | # Fix newlines (make them all the same) | |
840 | expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) | |
841 | ||
842 | # Actual output from router | |
843 | actual = net['r%s' % i].cmd('vtysh -c "show mpls ldp interface" 2> /dev/null').rstrip() | |
844 | # Mask out Timer in Uptime | |
845 | actual = re.sub(r" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual) | |
846 | # Fix newlines (make them all the same) | |
847 | actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) | |
848 | ||
849 | # Generate Diff | |
17070436 MW |
850 | diff = topotest.get_textdiff(actual, expected, |
851 | title1="actual MPLS LDP interface status", | |
852 | title2="expected MPLS LDP interface status") | |
4501fbca MW |
853 | |
854 | # Empty string if it matches, otherwise diff contains unified diff | |
855 | if diff: | |
856 | sys.stderr.write('r%s failed MPLS LDP Interface status Check:\n%s\n' % (i, diff)) | |
857 | failures += 1 | |
858 | else: | |
859 | print("r%s ok" % i) | |
860 | ||
861 | if failures>0: | |
862 | fatal_error = "MPLS LDP Interface status failed" | |
863 | ||
864 | assert failures == 0, "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff) | |
865 | ||
7e7fc73b MW |
866 | # Make sure that all daemons are running |
867 | for i in range(1, 2): | |
868 | fatal_error = net['r%s' % i].checkRouterRunning() | |
869 | assert fatal_error == "", fatal_error | |
870 | ||
4501fbca MW |
871 | # For debugging after starting FRR/Quagga daemons, uncomment the next line |
872 | # CLI(net) | |
873 | ||
874 | ||
875 | def test_shutdown_check_stderr(): | |
876 | global fatal_error | |
877 | global net | |
878 | ||
879 | # Skip if previous fatal error condition is raised | |
880 | if (fatal_error != ""): | |
881 | pytest.skip(fatal_error) | |
882 | ||
b2764f90 | 883 | print("\n\n** Verifying unexpected STDERR output from daemons") |
4501fbca MW |
884 | print("******************************************\n") |
885 | ||
886 | if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: | |
50c40bde MW |
887 | print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n") |
888 | pytest.skip('Skipping test for Stderr output') | |
4501fbca MW |
889 | |
890 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
891 | ||
50c40bde MW |
892 | print("thisDir=" + thisDir) |
893 | ||
4501fbca MW |
894 | net['r1'].stopRouter() |
895 | ||
896 | log = net['r1'].getStdErr('ripd') | |
8e957dbb MW |
897 | if log: |
898 | print("\nRIPd StdErr Log:\n" + log) | |
4501fbca | 899 | log = net['r1'].getStdErr('ripngd') |
8e957dbb MW |
900 | if log: |
901 | print("\nRIPngd StdErr Log:\n" + log) | |
4501fbca | 902 | log = net['r1'].getStdErr('ospfd') |
8e957dbb MW |
903 | if log: |
904 | print("\nOSPFd StdErr Log:\n" + log) | |
4501fbca | 905 | log = net['r1'].getStdErr('ospf6d') |
8e957dbb MW |
906 | if log: |
907 | print("\nOSPF6d StdErr Log:\n" + log) | |
4501fbca | 908 | log = net['r1'].getStdErr('isisd') |
8e957dbb MW |
909 | if log: |
910 | print("\nISISd StdErr Log:\n" + log) | |
4501fbca | 911 | log = net['r1'].getStdErr('bgpd') |
8e957dbb MW |
912 | if log: |
913 | print("\nBGPd StdErr Log:\n" + log) | |
4501fbca MW |
914 | if (net['r1'].daemon_available('ldpd')): |
915 | log = net['r1'].getStdErr('ldpd') | |
8e957dbb MW |
916 | if log: |
917 | print("\nLDPd StdErr Log:\n" + log) | |
4501fbca | 918 | log = net['r1'].getStdErr('zebra') |
8e957dbb MW |
919 | if log: |
920 | print("\nZebra StdErr Log:\n" + log) | |
4501fbca MW |
921 | |
922 | ||
50c40bde MW |
923 | def test_shutdown_check_memleak(): |
924 | global fatal_error | |
925 | global net | |
926 | ||
927 | # Skip if previous fatal error condition is raised | |
928 | if (fatal_error != ""): | |
929 | pytest.skip(fatal_error) | |
930 | ||
931 | if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None: | |
932 | print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n") | |
933 | pytest.skip('Skipping test for memory leaks') | |
934 | ||
935 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
936 | ||
937 | for i in range(1, 2): | |
938 | net['r%s' % i].stopRouter() | |
939 | net['r%s' % i].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__)) | |
940 | ||
941 | ||
4501fbca MW |
942 | if __name__ == '__main__': |
943 | ||
944 | setLogLevel('info') | |
945 | # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli | |
946 | # retval = pytest.main(["-s", "--tb=no"]) | |
947 | retval = pytest.main(["-s"]) | |
948 | sys.exit(retval) |