]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/all_protocol_startup/test_all_protocol_startup.py
Merge pull request #9902 from LabNConsulting/working/lb/lutil-wait-optimization
[mirror_frr.git] / tests / topotests / all_protocol_startup / test_all_protocol_startup.py
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
33 import pytest
34 import glob
35 from time import sleep
36
37 pytestmark = [
38 pytest.mark.babeld,
39 pytest.mark.bgpd,
40 pytest.mark.isisd,
41 pytest.mark.nhrpd,
42 pytest.mark.ospfd,
43 pytest.mark.pbrd,
44 pytest.mark.ripd,
45 ]
46
47 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
48 from lib import topotest
49 from lib.topogen import Topogen, get_topogen
50
51 fatal_error = ""
52
53
54 #####################################################
55 ##
56 ## Network Topology Definition
57 ##
58 #####################################################
59
60
61 def build_topo(tgen):
62 router = tgen.add_router("r1")
63 for i in range(0, 10):
64 tgen.add_switch("sw%d" % i).add_link(router)
65
66
67 #####################################################
68 ##
69 ## Tests starting
70 ##
71 #####################################################
72
73
74 def setup_module(module):
75 global fatal_error
76
77 print("\n\n** %s: Setup Topology" % module.__name__)
78 print("******************************************\n")
79
80 thisDir = os.path.dirname(os.path.realpath(__file__))
81 tgen = Topogen(build_topo, module.__name__)
82 tgen.start_topology()
83
84 net = tgen.net
85
86 if net["r1"].get_routertype() != "frr":
87 fatal_error = "Test is only implemented for FRR"
88 sys.stderr.write("\n\nTest is only implemented for FRR - Skipping\n\n")
89 pytest.skip(fatal_error)
90
91 # Starting Routers
92 #
93 # Main router
94 for i in range(1, 2):
95 net["r%s" % i].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir, i))
96 net["r%s" % i].loadConf("ripd", "%s/r%s/ripd.conf" % (thisDir, i))
97 net["r%s" % i].loadConf("ripngd", "%s/r%s/ripngd.conf" % (thisDir, i))
98 net["r%s" % i].loadConf("ospfd", "%s/r%s/ospfd.conf" % (thisDir, i))
99 if net["r1"].checkRouterVersion("<", "4.0"):
100 net["r%s" % i].loadConf(
101 "ospf6d", "%s/r%s/ospf6d.conf-pre-v4" % (thisDir, i)
102 )
103 else:
104 net["r%s" % i].loadConf("ospf6d", "%s/r%s/ospf6d.conf" % (thisDir, i))
105 net["r%s" % i].loadConf("isisd", "%s/r%s/isisd.conf" % (thisDir, i))
106 net["r%s" % i].loadConf("bgpd", "%s/r%s/bgpd.conf" % (thisDir, i))
107 if net["r%s" % i].daemon_available("ldpd"):
108 # Only test LDPd if it's installed and Kernel >= 4.5
109 net["r%s" % i].loadConf("ldpd", "%s/r%s/ldpd.conf" % (thisDir, i))
110 net["r%s" % i].loadConf("sharpd")
111 net["r%s" % i].loadConf("nhrpd", "%s/r%s/nhrpd.conf" % (thisDir, i))
112 net["r%s" % i].loadConf("babeld", "%s/r%s/babeld.conf" % (thisDir, i))
113 net["r%s" % i].loadConf("pbrd", "%s/r%s/pbrd.conf" % (thisDir, i))
114 tgen.gears["r%s" % i].start()
115
116 # For debugging after starting FRR daemons, uncomment the next line
117 # tgen.mininet_cli()
118
119
120 def teardown_module(module):
121 print("\n\n** %s: Shutdown Topology" % module.__name__)
122 print("******************************************\n")
123 tgen = get_topogen()
124 tgen.stop_topology()
125
126
127 def test_router_running():
128 global fatal_error
129 tgen = get_topogen()
130 net = tgen.net
131
132 # Skip if previous fatal error condition is raised
133 if fatal_error != "":
134 pytest.skip(fatal_error)
135
136 print("\n\n** Check if FRR is running on each Router node")
137 print("******************************************\n")
138 sleep(5)
139
140 # Starting Routers
141 for i in range(1, 2):
142 fatal_error = net["r%s" % i].checkRouterRunning()
143 assert fatal_error == "", fatal_error
144
145 # For debugging after starting FRR daemons, uncomment the next line
146 # tgen.mininet_cli()
147
148
149 def test_error_messages_vtysh():
150 global fatal_error
151 net = get_topogen().net
152
153 # Skip if previous fatal error condition is raised
154 if fatal_error != "":
155 pytest.skip(fatal_error)
156
157 print("\n\n** Check for error messages on VTYSH")
158 print("******************************************\n")
159
160 failures = 0
161 for i in range(1, 2):
162 #
163 # First checking Standard Output
164 #
165
166 # VTYSH output from router
167 vtystdout = net["r%s" % i].cmd('vtysh -c "show version" 2> /dev/null').rstrip()
168
169 # Fix newlines (make them all the same)
170 vtystdout = ("\n".join(vtystdout.splitlines()) + "\n").rstrip()
171 # Drop everything starting with "FRRouting X.xx" message
172 vtystdout = re.sub(r"FRRouting [0-9]+.*", "", vtystdout, flags=re.DOTALL)
173
174 if vtystdout == "":
175 print("r%s StdOut ok" % i)
176
177 assert vtystdout == "", "Vtysh StdOut Output check failed for router r%s" % i
178
179 #
180 # Second checking Standard Error
181 #
182
183 # VTYSH StdErr output from router
184 vtystderr = net["r%s" % i].cmd('vtysh -c "show version" > /dev/null').rstrip()
185
186 # Fix newlines (make them all the same)
187 vtystderr = ("\n".join(vtystderr.splitlines()) + "\n").rstrip()
188 # # Drop everything starting with "FRRouting X.xx" message
189 # vtystderr = re.sub(r"FRRouting [0-9]+.*", "", vtystderr, flags=re.DOTALL)
190
191 if vtystderr == "":
192 print("r%s StdErr ok" % i)
193
194 assert vtystderr == "", "Vtysh StdErr Output check failed for router r%s" % i
195
196 # Make sure that all daemons are running
197 for i in range(1, 2):
198 fatal_error = net["r%s" % i].checkRouterRunning()
199 assert fatal_error == "", fatal_error
200
201
202 def test_error_messages_daemons():
203 global fatal_error
204 net = get_topogen().net
205
206 # Skip if previous fatal error condition is raised
207 if fatal_error != "":
208 pytest.skip(fatal_error)
209
210 print("\n\n** Check for error messages in daemons")
211 print("******************************************\n")
212
213 error_logs = ""
214
215 for i in range(1, 2):
216 log = net["r%s" % i].getStdErr("ripd")
217 if log:
218 error_logs += "r%s RIPd StdErr Output:\n" % i
219 error_logs += log
220 log = net["r%s" % i].getStdErr("ripngd")
221 if log:
222 error_logs += "r%s RIPngd StdErr Output:\n" % i
223 error_logs += log
224 log = net["r%s" % i].getStdErr("ospfd")
225 if log:
226 error_logs += "r%s OSPFd StdErr Output:\n" % i
227 error_logs += log
228 log = net["r%s" % i].getStdErr("ospf6d")
229 if log:
230 error_logs += "r%s OSPF6d StdErr Output:\n" % i
231 error_logs += log
232 log = net["r%s" % i].getStdErr("isisd")
233 # ISIS shows debugging enabled status on StdErr
234 # Remove these messages
235 log = re.sub(r"^IS-IS .* debugging is on.*", "", log).rstrip()
236 if log:
237 error_logs += "r%s ISISd StdErr Output:\n" % i
238 error_logs += log
239 log = net["r%s" % i].getStdErr("bgpd")
240 if log:
241 error_logs += "r%s BGPd StdErr Output:\n" % i
242 error_logs += log
243 if net["r%s" % i].daemon_available("ldpd"):
244 log = net["r%s" % i].getStdErr("ldpd")
245 if log:
246 error_logs += "r%s LDPd StdErr Output:\n" % i
247 error_logs += log
248
249 log = net["r1"].getStdErr("nhrpd")
250 # NHRPD shows YANG model not embedded messages
251 # Ignore these
252 log = re.sub(r".*YANG model.*not embedded.*", "", log).rstrip()
253 if log:
254 error_logs += "r%s NHRPd StdErr Output:\n" % i
255 error_logs += log
256
257 log = net["r1"].getStdErr("babeld")
258 if log:
259 error_logs += "r%s BABELd StdErr Output:\n" % i
260 error_logs += log
261
262 log = net["r1"].getStdErr("pbrd")
263 if log:
264 error_logs += "r%s PBRd StdErr Output:\n" % i
265 error_logs += log
266
267 log = net["r%s" % i].getStdErr("zebra")
268 if log:
269 error_logs += "r%s Zebra StdErr Output:\n" % i
270 error_logs += log
271
272 if error_logs:
273 sys.stderr.write(
274 "Failed check for StdErr Output on daemons:\n%s\n" % error_logs
275 )
276
277 # Ignoring the issue if told to ignore (ie not yet fixed)
278 if error_logs != "":
279 if os.environ.get("bamboo_TOPOTESTS_ISSUE_349") == "IGNORE":
280 sys.stderr.write(
281 "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349\n"
282 )
283 pytest.skip(
284 "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349"
285 )
286
287 assert error_logs == "", "Daemons report errors to StdErr"
288
289
290 def test_converge_protocols():
291 global fatal_error
292 net = get_topogen().net
293
294 # Skip if previous fatal error condition is raised
295 if fatal_error != "":
296 pytest.skip(fatal_error)
297
298 thisDir = os.path.dirname(os.path.realpath(__file__))
299
300 print("\n\n** Waiting for protocols convergence")
301 print("******************************************\n")
302
303 # Not really implemented yet - just sleep 60 secs for now
304 sleep(60)
305
306 # Make sure that all daemons are running
307 failures = 0
308 for i in range(1, 2):
309 fatal_error = net["r%s" % i].checkRouterRunning()
310 assert fatal_error == "", fatal_error
311
312 print("Show that v4 routes are right\n")
313 v4_routesFile = "%s/r%s/ipv4_routes.ref" % (thisDir, i)
314 expected = (
315 net["r%s" % i].cmd("sort {} 2> /dev/null".format(v4_routesFile)).rstrip()
316 )
317 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
318
319 actual = (
320 net["r%s" % i]
321 .cmd(
322 "vtysh -c \"show ip route\" | sed -e '/^Codes: /,/^\\s*$/d' | sort 2> /dev/null"
323 )
324 .rstrip()
325 )
326 # Drop time in last update
327 actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
328 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
329 diff = topotest.get_textdiff(
330 actual,
331 expected,
332 title1="Actual IP Routing Table",
333 title2="Expected IP RoutingTable",
334 )
335 if diff:
336 sys.stderr.write("r%s failed IP Routing table check:\n%s\n" % (i, diff))
337 failures += 1
338 else:
339 print("r%s ok" % i)
340
341 assert failures == 0, "IP Routing table failed for r%s\n%s" % (i, diff)
342
343 failures = 0
344
345 print("Show that v6 routes are right\n")
346 v6_routesFile = "%s/r%s/ipv6_routes.ref" % (thisDir, i)
347 expected = (
348 net["r%s" % i].cmd("sort {} 2> /dev/null".format(v6_routesFile)).rstrip()
349 )
350 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
351
352 actual = (
353 net["r%s" % i]
354 .cmd(
355 "vtysh -c \"show ipv6 route\" | sed -e '/^Codes: /,/^\\s*$/d' | sort 2> /dev/null"
356 )
357 .rstrip()
358 )
359 # Drop time in last update
360 actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
361 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
362 diff = topotest.get_textdiff(
363 actual,
364 expected,
365 title1="Actual IPv6 Routing Table",
366 title2="Expected IPv6 RoutingTable",
367 )
368 if diff:
369 sys.stderr.write("r%s failed IPv6 Routing table check:\n%s\n" % (i, diff))
370 failures += 1
371 else:
372 print("r%s ok" % i)
373
374 assert failures == 0, "IPv6 Routing table failed for r%s\n%s" % (i, diff)
375
376
377 def route_get_nhg_id(route_str):
378 net = get_topogen().net
379 output = net["r1"].cmd('vtysh -c "show ip route %s nexthop-group"' % route_str)
380 match = re.search(r"Nexthop Group ID: (\d+)", output)
381 assert match is not None, (
382 "Nexthop Group ID not found for sharpd route %s" % route_str
383 )
384
385 nhg_id = int(match.group(1))
386 return nhg_id
387
388
389 def verify_nexthop_group(nhg_id, recursive=False, ecmp=0):
390 net = get_topogen().net
391 count = 0
392 valid = None
393 ecmpcount = None
394 depends = None
395 resolved_id = None
396 installed = None
397 found = False
398
399 while not found and count < 10:
400 count += 1
401 # Verify NHG is valid/installed
402 output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id)
403 valid = re.search(r"Valid", output)
404 if valid is None:
405 found = False
406 sleep(1)
407 continue
408
409 if ecmp or recursive:
410 ecmpcount = re.search(r"Depends:.*\n", output)
411 if ecmpcount is None:
412 found = False
413 sleep(1)
414 continue
415
416 # list of IDs in group
417 depends = re.findall(r"\((\d+)\)", ecmpcount.group(0))
418
419 if ecmp:
420 if len(depends) != ecmp:
421 found = False
422 sleep(1)
423 continue
424 else:
425 # If recursive, we need to look at its resolved group
426 if len(depends) != 1:
427 found = False
428 sleep(1)
429 continue
430
431 resolved_id = int(depends[0])
432 verify_nexthop_group(resolved_id, False)
433 else:
434 installed = re.search(r"Installed", output)
435 if installed is None:
436 found = False
437 sleep(1)
438 continue
439 found = True
440
441 assert valid is not None, "Nexthop Group ID=%d not marked Valid" % nhg_id
442 if ecmp or recursive:
443 assert ecmpcount is not None, "Nexthop Group ID=%d has no depends" % nhg_id
444 if ecmp:
445 assert len(depends) == ecmp, (
446 "Nexthop Group ID=%d doesn't match ecmp size" % nhg_id
447 )
448 else:
449 assert len(depends) == 1, (
450 "Nexthop Group ID=%d should only have one recursive depend" % nhg_id
451 )
452 else:
453 assert installed is not None, (
454 "Nexthop Group ID=%d not marked Installed" % nhg_id
455 )
456
457
458 def verify_route_nexthop_group(route_str, recursive=False, ecmp=0):
459 # Verify route and that zebra created NHGs for and they are valid/installed
460 nhg_id = route_get_nhg_id(route_str)
461 verify_nexthop_group(nhg_id, recursive, ecmp)
462
463
464 def test_nexthop_groups():
465 global fatal_error
466 net = get_topogen().net
467
468 # Skip if previous fatal error condition is raised
469 if fatal_error != "":
470 pytest.skip(fatal_error)
471
472 print("\n\n** Verifying Nexthop Groups")
473 print("******************************************\n")
474
475 ### Nexthop Group Tests
476
477 ## Basic test
478
479 # Create a lib nexthop-group
480 net["r1"].cmd(
481 'vtysh -c "c t" -c "nexthop-group basic" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"'
482 )
483
484 # Create with sharpd using nexthop-group
485 net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.1 nexthop-group basic 1"')
486 verify_route_nexthop_group("2.2.2.1/32")
487
488 ## Connected
489
490 net["r1"].cmd(
491 'vtysh -c "c t" -c "nexthop-group connected" -c "nexthop r1-eth1" -c "nexthop r1-eth2"'
492 )
493
494 net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.2 nexthop-group connected 1"')
495 verify_route_nexthop_group("2.2.2.2/32")
496
497 ## Recursive
498
499 net["r1"].cmd(
500 'vtysh -c "c t" -c "nexthop-group basic-recursive" -c "nexthop 2.2.2.1"'
501 )
502
503 net["r1"].cmd(
504 'vtysh -c "sharp install routes 3.3.3.1 nexthop-group basic-recursive 1"'
505 )
506
507 verify_route_nexthop_group("3.3.3.1/32", True)
508
509 ## Duplicate
510
511 net["r1"].cmd(
512 'vtysh -c "c t" -c "nexthop-group duplicate" -c "nexthop 2.2.2.1" -c "nexthop 1.1.1.1"'
513 )
514
515 net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.2 nexthop-group duplicate 1"')
516
517 verify_route_nexthop_group("3.3.3.2/32")
518
519 ## Two 4-Way ECMP
520
521 net["r1"].cmd(
522 'vtysh -c "c t" -c "nexthop-group fourA" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2" \
523 -c "nexthop 1.1.1.3" -c "nexthop 1.1.1.4"'
524 )
525
526 net["r1"].cmd('vtysh -c "sharp install routes 4.4.4.1 nexthop-group fourA 1"')
527
528 verify_route_nexthop_group("4.4.4.1/32")
529
530 net["r1"].cmd(
531 'vtysh -c "c t" -c "nexthop-group fourB" -c "nexthop 1.1.1.5" -c "nexthop 1.1.1.6" \
532 -c "nexthop 1.1.1.7" -c "nexthop 1.1.1.8"'
533 )
534
535 net["r1"].cmd('vtysh -c "sharp install routes 4.4.4.2 nexthop-group fourB 1"')
536
537 verify_route_nexthop_group("4.4.4.2/32")
538
539 ## Recursive to 8-Way ECMP
540
541 net["r1"].cmd(
542 'vtysh -c "c t" -c "nexthop-group eight-recursive" -c "nexthop 4.4.4.1" -c "nexthop 4.4.4.2"'
543 )
544
545 net["r1"].cmd(
546 'vtysh -c "sharp install routes 5.5.5.1 nexthop-group eight-recursive 1"'
547 )
548
549 verify_route_nexthop_group("5.5.5.1/32")
550
551 ## 4-way ECMP Routes Pointing to Each Other
552
553 # This is to check for a bug with NH resolution where
554 # routes would infintely resolve to each other blowing
555 # up the resolved-> nexthop pointer.
556
557 net["r1"].cmd(
558 'vtysh -c "c t" -c "nexthop-group infinite-recursive" -c "nexthop 6.6.6.1" -c "nexthop 6.6.6.2" \
559 -c "nexthop 6.6.6.3" -c "nexthop 6.6.6.4"'
560 )
561
562 # static route nexthops can recurse to
563
564 net["r1"].cmd('vtysh -c "c t" -c "ip route 6.6.6.0/24 1.1.1.1"')
565
566 # Make routes that point to themselves in ecmp
567
568 net["r1"].cmd(
569 'vtysh -c "sharp install routes 6.6.6.4 nexthop-group infinite-recursive 1"'
570 )
571
572 net["r1"].cmd(
573 'vtysh -c "sharp install routes 6.6.6.3 nexthop-group infinite-recursive 1"'
574 )
575
576 net["r1"].cmd(
577 'vtysh -c "sharp install routes 6.6.6.2 nexthop-group infinite-recursive 1"'
578 )
579
580 net["r1"].cmd(
581 'vtysh -c "sharp install routes 6.6.6.1 nexthop-group infinite-recursive 1"'
582 )
583
584 # Get routes and test if has too many (duplicate) nexthops
585 count = 0
586 dups = []
587 nhg_id = route_get_nhg_id("6.6.6.1/32")
588 while (len(dups) != 3) and count < 10:
589 output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id)
590
591 dups = re.findall(r"(via 1\.1\.1\.1)", output)
592 if len(dups) != 3:
593 count += 1
594 sleep(1)
595
596 # Should find 3, itself is inactive
597 assert len(dups) == 3, (
598 "Route 6.6.6.1/32 with Nexthop Group ID=%d has wrong number of resolved nexthops"
599 % nhg_id
600 )
601
602 ## Remove all NHG routes
603
604 net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.1 1"')
605 net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.2 1"')
606 net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.1 1"')
607 net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.2 1"')
608 net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.1 1"')
609 net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.2 1"')
610 net["r1"].cmd('vtysh -c "sharp remove routes 5.5.5.1 1"')
611 net["r1"].cmd('vtysh -c "sharp remove routes 6.6.6.1 4"')
612 net["r1"].cmd('vtysh -c "c t" -c "no ip route 6.6.6.0/24 1.1.1.1"')
613
614
615 def test_rip_status():
616 global fatal_error
617 net = get_topogen().net
618
619 # Skip if previous fatal error condition is raised
620 if fatal_error != "":
621 pytest.skip(fatal_error)
622
623 thisDir = os.path.dirname(os.path.realpath(__file__))
624
625 print("\n\n** Verifying RIP status")
626 print("******************************************\n")
627 failures = 0
628 for i in range(1, 2):
629 refTableFile = "%s/r%s/rip_status.ref" % (thisDir, i)
630 if os.path.isfile(refTableFile):
631 # Read expected result from file
632 expected = open(refTableFile).read().rstrip()
633 # Fix newlines (make them all the same)
634 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
635
636 # Actual output from router
637 actual = (
638 net["r%s" % i]
639 .cmd('vtysh -c "show ip rip status" 2> /dev/null')
640 .rstrip()
641 )
642 # Drop time in next due
643 actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
644 # Drop time in last update
645 actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
646 # Fix newlines (make them all the same)
647 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
648
649 # Generate Diff
650 diff = topotest.get_textdiff(
651 actual,
652 expected,
653 title1="actual IP RIP status",
654 title2="expected IP RIP status",
655 )
656
657 # Empty string if it matches, otherwise diff contains unified diff
658 if diff:
659 sys.stderr.write("r%s failed IP RIP status check:\n%s\n" % (i, diff))
660 failures += 1
661 else:
662 print("r%s ok" % i)
663
664 assert failures == 0, "IP RIP status failed for router r%s:\n%s" % (i, diff)
665
666 # Make sure that all daemons are running
667 for i in range(1, 2):
668 fatal_error = net["r%s" % i].checkRouterRunning()
669 assert fatal_error == "", fatal_error
670
671
672 def test_ripng_status():
673 global fatal_error
674 net = get_topogen().net
675
676 # Skip if previous fatal error condition is raised
677 if fatal_error != "":
678 pytest.skip(fatal_error)
679
680 thisDir = os.path.dirname(os.path.realpath(__file__))
681
682 print("\n\n** Verifying RIPng status")
683 print("******************************************\n")
684 failures = 0
685 for i in range(1, 2):
686 refTableFile = "%s/r%s/ripng_status.ref" % (thisDir, i)
687 if os.path.isfile(refTableFile):
688 # Read expected result from file
689 expected = open(refTableFile).read().rstrip()
690 # Fix newlines (make them all the same)
691 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
692
693 # Actual output from router
694 actual = (
695 net["r%s" % i]
696 .cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null')
697 .rstrip()
698 )
699 # Mask out Link-Local mac address portion. They are random...
700 actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual)
701 # Drop time in next due
702 actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
703 # Drop time in last update
704 actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
705 # Fix newlines (make them all the same)
706 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
707
708 # Generate Diff
709 diff = topotest.get_textdiff(
710 actual,
711 expected,
712 title1="actual IPv6 RIPng status",
713 title2="expected IPv6 RIPng status",
714 )
715
716 # Empty string if it matches, otherwise diff contains unified diff
717 if diff:
718 sys.stderr.write(
719 "r%s failed IPv6 RIPng status check:\n%s\n" % (i, diff)
720 )
721 failures += 1
722 else:
723 print("r%s ok" % i)
724
725 assert failures == 0, "IPv6 RIPng status failed for router r%s:\n%s" % (
726 i,
727 diff,
728 )
729
730 # Make sure that all daemons are running
731 for i in range(1, 2):
732 fatal_error = net["r%s" % i].checkRouterRunning()
733 assert fatal_error == "", fatal_error
734
735
736 def test_ospfv2_interfaces():
737 global fatal_error
738 net = get_topogen().net
739
740 # Skip if previous fatal error condition is raised
741 if fatal_error != "":
742 pytest.skip(fatal_error)
743
744 thisDir = os.path.dirname(os.path.realpath(__file__))
745
746 print("\n\n** Verifying OSPFv2 interfaces")
747 print("******************************************\n")
748 failures = 0
749 for i in range(1, 2):
750 refTableFile = "%s/r%s/show_ip_ospf_interface.ref" % (thisDir, i)
751 if os.path.isfile(refTableFile):
752 # Read expected result from file
753 expected = open(refTableFile).read().rstrip()
754 # Fix newlines (make them all the same)
755 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
756
757 # Actual output from router
758 actual = (
759 net["r%s" % i]
760 .cmd('vtysh -c "show ip ospf interface" 2> /dev/null')
761 .rstrip()
762 )
763 # Mask out Bandwidth portion. They may change..
764 actual = re.sub(r"BW [0-9]+ Mbit", "BW XX Mbit", actual)
765 actual = re.sub(r"ifindex [0-9]+", "ifindex X", actual)
766
767 # Drop time in next due
768 actual = re.sub(r"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual)
769 actual = re.sub(
770 r"Hello due in [0-9\.]+ usecs", "Hello due in XX.XXXs", actual
771 )
772 # Fix 'MTU mismatch detection: enabled' vs 'MTU mismatch detection:enabled' - accept both
773 actual = re.sub(
774 r"MTU mismatch detection:([a-z]+.*)",
775 r"MTU mismatch detection: \1",
776 actual,
777 )
778 # Fix newlines (make them all the same)
779 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
780
781 # Generate Diff
782 diff = topotest.get_textdiff(
783 actual,
784 expected,
785 title1="actual SHOW IP OSPF INTERFACE",
786 title2="expected SHOW IP OSPF INTERFACE",
787 )
788
789 # Empty string if it matches, otherwise diff contains unified diff
790 if diff:
791 sys.stderr.write(
792 "r%s failed SHOW IP OSPF INTERFACE check:\n%s\n" % (i, diff)
793 )
794 failures += 1
795 else:
796 print("r%s ok" % i)
797
798 # Ignoring the issue if told to ignore (ie not yet fixed)
799 if failures != 0:
800 if os.environ.get("bamboo_TOPOTESTS_ISSUE_348") == "IGNORE":
801 sys.stderr.write(
802 "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348\n"
803 )
804 pytest.skip(
805 "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348"
806 )
807
808 assert (
809 failures == 0
810 ), "SHOW IP OSPF INTERFACE failed for router r%s:\n%s" % (i, diff)
811
812 # Make sure that all daemons are running
813 for i in range(1, 2):
814 fatal_error = net["r%s" % i].checkRouterRunning()
815 assert fatal_error == "", fatal_error
816
817
818 def test_isis_interfaces():
819 global fatal_error
820 net = get_topogen().net
821
822 # Skip if previous fatal error condition is raised
823 if fatal_error != "":
824 pytest.skip(fatal_error)
825
826 thisDir = os.path.dirname(os.path.realpath(__file__))
827
828 print("\n\n** Verifying ISIS interfaces")
829 print("******************************************\n")
830 failures = 0
831 for i in range(1, 2):
832 refTableFile = "%s/r%s/show_isis_interface_detail.ref" % (thisDir, i)
833 if os.path.isfile(refTableFile):
834 # Read expected result from file
835 expected = open(refTableFile).read().rstrip()
836 # Fix newlines (make them all the same)
837 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
838
839 # Actual output from router
840 actual = (
841 net["r%s" % i]
842 .cmd('vtysh -c "show isis interface detail" 2> /dev/null')
843 .rstrip()
844 )
845 # Mask out Link-Local mac address portion. They are random...
846 actual = re.sub(r"fe80::[0-9a-f:]+", "fe80::XXXX:XXXX:XXXX:XXXX", actual)
847 # Mask out SNPA mac address portion. They are random...
848 actual = re.sub(r"SNPA: [0-9a-f\.]+", "SNPA: XXXX.XXXX.XXXX", actual)
849 # Mask out Circuit ID number
850 actual = re.sub(r"Circuit Id: 0x[0-9a-f]+", "Circuit Id: 0xXX", actual)
851 # Fix newlines (make them all the same)
852 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
853
854 # Generate Diff
855 diff = topotest.get_textdiff(
856 actual,
857 expected,
858 title1="actual SHOW ISIS INTERFACE DETAIL",
859 title2="expected SHOW ISIS OSPF6 INTERFACE DETAIL",
860 )
861
862 # Empty string if it matches, otherwise diff contains unified diff
863 if diff:
864 sys.stderr.write(
865 "r%s failed SHOW ISIS INTERFACE DETAIL check:\n%s\n" % (i, diff)
866 )
867 failures += 1
868 else:
869 print("r%s ok" % i)
870
871 assert (
872 failures == 0
873 ), "SHOW ISIS INTERFACE DETAIL failed for router r%s:\n%s" % (i, diff)
874
875 # Make sure that all daemons are running
876 for i in range(1, 2):
877 fatal_error = net["r%s" % i].checkRouterRunning()
878 assert fatal_error == "", fatal_error
879
880
881 def test_bgp_summary():
882 global fatal_error
883 net = get_topogen().net
884
885 # Skip if previous fatal error condition is raised
886 if fatal_error != "":
887 pytest.skip(fatal_error)
888
889 thisDir = os.path.dirname(os.path.realpath(__file__))
890
891 print("\n\n** Verifying BGP Summary")
892 print("******************************************\n")
893 failures = 0
894 for i in range(1, 2):
895 refTableFile = "%s/r%s/show_ip_bgp_summary.ref" % (thisDir, i)
896 if os.path.isfile(refTableFile):
897 # Read expected result from file
898 expected_original = open(refTableFile).read().rstrip()
899
900 for arguments in [
901 "",
902 "remote-as internal",
903 "remote-as external",
904 "remote-as 100",
905 "remote-as 123",
906 "neighbor 192.168.7.10",
907 "neighbor 192.168.7.10",
908 "neighbor fc00:0:0:8::1000",
909 "neighbor 10.0.0.1",
910 "terse",
911 "remote-as internal terse",
912 "remote-as external terse",
913 "remote-as 100 terse",
914 "remote-as 123 terse",
915 "neighbor 192.168.7.10 terse",
916 "neighbor 192.168.7.10 terse",
917 "neighbor fc00:0:0:8::1000 terse",
918 "neighbor 10.0.0.1 terse",
919 ]:
920 # Actual output from router
921 actual = (
922 net["r%s" % i]
923 .cmd(
924 'vtysh -c "show ip bgp summary ' + arguments + '" 2> /dev/null'
925 )
926 .rstrip()
927 )
928
929 # Mask out "using XXiXX bytes" portion. They are random...
930 actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual)
931 # Mask out "using XiXXX KiB" portion. They are random...
932 actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual)
933
934 # Remove extra summaries which exist with newer versions
935
936 # Remove summary lines (changed recently)
937 actual = re.sub(r"Total number.*", "", actual)
938 actual = re.sub(r"Displayed.*", "", actual)
939 # Remove IPv4 Unicast Summary (Title only)
940 actual = re.sub(r"IPv4 Unicast Summary \(VRF default\):", "", actual)
941 # Remove IPv4 Multicast Summary (all of it)
942 actual = re.sub(r"IPv4 Multicast Summary \(VRF default\):", "", actual)
943 actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual)
944 # Remove IPv4 VPN Summary (all of it)
945 actual = re.sub(r"IPv4 VPN Summary \(VRF default\):", "", actual)
946 actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual)
947 # Remove IPv4 Encap Summary (all of it)
948 actual = re.sub(r"IPv4 Encap Summary \(VRF default\):", "", actual)
949 actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual)
950 # Remove Unknown Summary (all of it)
951 actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual)
952 actual = re.sub(r"No Unknown neighbor is configured", "", actual)
953 # Make Connect/Active/Idle the same (change them all to Active)
954 actual = re.sub(r" Connect ", " Active ", actual)
955 actual = re.sub(r" Idle ", " Active ", actual)
956
957 actual = re.sub(
958 r"IPv4 labeled-unicast Summary \(VRF default\):", "", actual
959 )
960 actual = re.sub(
961 r"No IPv4 labeled-unicast neighbor is configured", "", actual
962 )
963
964 expected = expected_original
965 # apply argumentss on expected output
966 if "internal" in arguments or "remote-as 100" in arguments:
967 expected = re.sub(r".+\s+200\s+.+", "", expected)
968 elif "external" in arguments:
969 expected = re.sub(r".+\s+100\s+.+Active.+", "", expected)
970 elif "remote-as 123" in arguments:
971 expected = re.sub(
972 r"(192.168.7.(1|2)0|fc00:0:0:8::(1|2)000).+Active.+",
973 "",
974 expected,
975 )
976 expected = re.sub(r"\nNeighbor.+Desc", "", expected)
977 expected = expected + "% No matching neighbor\n"
978 elif "192.168.7.10" in arguments:
979 expected = re.sub(
980 r"(192.168.7.20|fc00:0:0:8::(1|2)000).+Active.+", "", expected
981 )
982 elif "fc00:0:0:8::1000" in arguments:
983 expected = re.sub(
984 r"(192.168.7.(1|2)0|fc00:0:0:8::2000).+Active.+", "", expected
985 )
986 elif "10.0.0.1" in arguments:
987 expected = "No such neighbor in this view/vrf"
988
989 if "terse" in arguments:
990 expected = re.sub(r"BGP table version .+", "", expected)
991 expected = re.sub(r"RIB entries .+", "", expected)
992 expected = re.sub(r"Peers [0-9]+, using .+", "", expected)
993
994 # Strip empty lines
995 actual = actual.lstrip().rstrip()
996 expected = expected.lstrip().rstrip()
997 actual = re.sub(r"\n+", "\n", actual)
998 expected = re.sub(r"\n+", "\n", expected)
999
1000 # reapply initial formatting
1001 if "terse" in arguments:
1002 actual = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", actual)
1003 expected = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", expected)
1004 else:
1005 actual = re.sub(r"KiB of memory\n", "KiB of memory\n\n", actual)
1006 expected = re.sub(r"KiB of memory\n", "KiB of memory\n\n", expected)
1007
1008 # realign expected neighbor columns if needed
1009 try:
1010 idx_actual = (
1011 re.search(r"(Neighbor\s+V\s+)", actual).group(1).find("V")
1012 )
1013 idx_expected = (
1014 re.search(r"(Neighbor\s+V\s+)", expected).group(1).find("V")
1015 )
1016 idx_diff = idx_expected - idx_actual
1017 if idx_diff > 0:
1018 # Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
1019 expected = re.sub(" " * idx_diff + "V ", "V ", expected)
1020 # 192.168.7.10 4 100 0 0 0 0 0 never Active
1021 expected = re.sub(" " * idx_diff + "4 ", "4 ", expected)
1022 except AttributeError:
1023 pass
1024
1025 # Fix newlines (make them all the same)
1026 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1027 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1028
1029 # Generate Diff
1030 diff = topotest.get_textdiff(
1031 actual,
1032 expected,
1033 title1="actual SHOW IP BGP SUMMARY " + arguments.upper(),
1034 title2="expected SHOW IP BGP SUMMARY " + arguments.upper(),
1035 )
1036
1037 # Empty string if it matches, otherwise diff contains unified diff
1038 if diff:
1039 sys.stderr.write(
1040 "r%s failed SHOW IP BGP SUMMARY check:\n%s\n" % (i, diff)
1041 )
1042 failures += 1
1043 else:
1044 print("r%s ok" % i)
1045
1046 assert (
1047 failures == 0
1048 ), "SHOW IP BGP SUMMARY failed for router r%s:\n%s" % (
1049 i,
1050 diff,
1051 )
1052
1053 # Make sure that all daemons are running
1054 for i in range(1, 2):
1055 fatal_error = net["r%s" % i].checkRouterRunning()
1056 assert fatal_error == "", fatal_error
1057
1058
1059 def test_bgp_ipv6_summary():
1060 global fatal_error
1061 net = get_topogen().net
1062
1063 # Skip if previous fatal error condition is raised
1064 if fatal_error != "":
1065 pytest.skip(fatal_error)
1066
1067 thisDir = os.path.dirname(os.path.realpath(__file__))
1068
1069 print("\n\n** Verifying BGP IPv6 Summary")
1070 print("******************************************\n")
1071 failures = 0
1072 for i in range(1, 2):
1073 refTableFile = "%s/r%s/show_bgp_ipv6_summary.ref" % (thisDir, i)
1074 if os.path.isfile(refTableFile):
1075 # Read expected result from file
1076 expected = open(refTableFile).read().rstrip()
1077 # Fix newlines (make them all the same)
1078 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1079
1080 # Actual output from router
1081 actual = (
1082 net["r%s" % i]
1083 .cmd('vtysh -c "show bgp ipv6 summary" 2> /dev/null')
1084 .rstrip()
1085 )
1086 # Mask out "using XXiXX bytes" portion. They are random...
1087 actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual)
1088 # Mask out "using XiXXX KiB" portion. They are random...
1089 actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual)
1090 #
1091 # Remove extra summaries which exist with newer versions
1092 #
1093 # Remove summary lines (changed recently)
1094 actual = re.sub(r"Total number.*", "", actual)
1095 actual = re.sub(r"Displayed.*", "", actual)
1096 # Remove IPv4 Unicast Summary (Title only)
1097 actual = re.sub(r"IPv6 Unicast Summary \(VRF default\):", "", actual)
1098 # Remove IPv4 Multicast Summary (all of it)
1099 actual = re.sub(r"IPv6 Multicast Summary \(VRF default\):", "", actual)
1100 actual = re.sub(r"No IPv6 Multicast neighbor is configured", "", actual)
1101 # Remove IPv4 VPN Summary (all of it)
1102 actual = re.sub(r"IPv6 VPN Summary \(VRF default\):", "", actual)
1103 actual = re.sub(r"No IPv6 VPN neighbor is configured", "", actual)
1104 # Remove IPv4 Encap Summary (all of it)
1105 actual = re.sub(r"IPv6 Encap Summary \(VRF default\):", "", actual)
1106 actual = re.sub(r"No IPv6 Encap neighbor is configured", "", actual)
1107 # Remove Unknown Summary (all of it)
1108 actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual)
1109 actual = re.sub(r"No Unknown neighbor is configured", "", actual)
1110 # Make Connect/Active/Idle the same (change them all to Active)
1111 actual = re.sub(r" Connect ", " Active ", actual)
1112 actual = re.sub(r" Idle ", " Active ", actual)
1113
1114 # Remove Labeled Unicast Summary (all of it)
1115 actual = re.sub(
1116 r"IPv6 labeled-unicast Summary \(VRF default\):", "", actual
1117 )
1118 actual = re.sub(
1119 r"No IPv6 labeled-unicast neighbor is configured", "", actual
1120 )
1121
1122 # Strip empty lines
1123 actual = actual.lstrip()
1124 actual = actual.rstrip()
1125 #
1126 # Fix newlines (make them all the same)
1127 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1128
1129 # Generate Diff
1130 diff = topotest.get_textdiff(
1131 actual,
1132 expected,
1133 title1="actual SHOW BGP IPv6 SUMMARY",
1134 title2="expected SHOW BGP IPv6 SUMMARY",
1135 )
1136
1137 # Empty string if it matches, otherwise diff contains unified diff
1138 if diff:
1139 sys.stderr.write(
1140 "r%s failed SHOW BGP IPv6 SUMMARY check:\n%s\n" % (i, diff)
1141 )
1142 failures += 1
1143 else:
1144 print("r%s ok" % i)
1145
1146 assert failures == 0, "SHOW BGP IPv6 SUMMARY failed for router r%s:\n%s" % (
1147 i,
1148 diff,
1149 )
1150
1151 # Make sure that all daemons are running
1152 for i in range(1, 2):
1153 fatal_error = net["r%s" % i].checkRouterRunning()
1154 assert fatal_error == "", fatal_error
1155
1156
1157 def test_nht():
1158 global fatal_error
1159 net = get_topogen().net
1160
1161 # Skip if previous fatal error condition is raised
1162 if fatal_error != "":
1163 pytest.skip(fatal_error)
1164
1165 print("\n\n**** Test that nexthop tracking is at least nominally working ****\n")
1166
1167 thisDir = os.path.dirname(os.path.realpath(__file__))
1168
1169 for i in range(1, 2):
1170 nhtFile = "%s/r%s/ip_nht.ref" % (thisDir, i)
1171 expected = open(nhtFile).read().rstrip()
1172 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1173
1174 actual = net["r%s" % i].cmd('vtysh -c "show ip nht" 2> /dev/null').rstrip()
1175 actual = re.sub(r"fd [0-9]+", "fd XX", actual)
1176 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1177
1178 diff = topotest.get_textdiff(
1179 actual,
1180 expected,
1181 title1="Actual `show ip nht`",
1182 title2="Expected `show ip nht`",
1183 )
1184
1185 if diff:
1186 assert 0, "r%s failed ip nht check:\n%s\n" % (i, diff)
1187 else:
1188 print("show ip nht is ok\n")
1189
1190 nhtFile = "%s/r%s/ipv6_nht.ref" % (thisDir, i)
1191 expected = open(nhtFile).read().rstrip()
1192 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1193
1194 actual = net["r%s" % i].cmd('vtysh -c "show ipv6 nht" 2> /dev/null').rstrip()
1195 actual = re.sub(r"fd [0-9]+", "fd XX", actual)
1196 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1197
1198 diff = topotest.get_textdiff(
1199 actual,
1200 expected,
1201 title1="Actual `show ip nht`",
1202 title2="Expected `show ip nht`",
1203 )
1204
1205 if diff:
1206 assert 0, "r%s failed ipv6 nht check:\n%s\n" % (i, diff)
1207 else:
1208 print("show ipv6 nht is ok\n")
1209
1210
1211 def test_bgp_ipv4():
1212 global fatal_error
1213 net = get_topogen().net
1214
1215 # Skip if previous fatal error condition is raised
1216 if fatal_error != "":
1217 pytest.skip(fatal_error)
1218
1219 thisDir = os.path.dirname(os.path.realpath(__file__))
1220
1221 print("\n\n** Verifying BGP IPv4")
1222 print("******************************************\n")
1223 diffresult = {}
1224 for i in range(1, 2):
1225 success = 0
1226 for refTableFile in glob.glob("%s/r%s/show_bgp_ipv4*.ref" % (thisDir, i)):
1227 if os.path.isfile(refTableFile):
1228 # Read expected result from file
1229 expected = open(refTableFile).read().rstrip()
1230 # Fix newlines (make them all the same)
1231 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1232
1233 # Actual output from router
1234 actual = (
1235 net["r%s" % i].cmd('vtysh -c "show bgp ipv4" 2> /dev/null').rstrip()
1236 )
1237 # Remove summary line (changed recently)
1238 actual = re.sub(r"Total number.*", "", actual)
1239 actual = re.sub(r"Displayed.*", "", actual)
1240 actual = actual.rstrip()
1241 # Fix newlines (make them all the same)
1242 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1243
1244 # Generate Diff
1245 diff = topotest.get_textdiff(
1246 actual,
1247 expected,
1248 title1="actual SHOW BGP IPv4",
1249 title2="expected SHOW BGP IPv4",
1250 )
1251
1252 # Empty string if it matches, otherwise diff contains unified diff
1253 if diff:
1254 diffresult[refTableFile] = diff
1255 else:
1256 success = 1
1257 print("template %s matched: r%s ok" % (refTableFile, i))
1258 break
1259
1260 if not success:
1261 resultstr = "No template matched.\n"
1262 for f in diffresult.keys():
1263 resultstr += "template %s: r%s failed SHOW BGP IPv4 check:\n%s\n" % (
1264 f,
1265 i,
1266 diffresult[f],
1267 )
1268 raise AssertionError(
1269 "SHOW BGP IPv4 failed for router r%s:\n%s" % (i, resultstr)
1270 )
1271
1272 # Make sure that all daemons are running
1273 for i in range(1, 2):
1274 fatal_error = net["r%s" % i].checkRouterRunning()
1275 assert fatal_error == "", fatal_error
1276
1277
1278 def test_bgp_ipv6():
1279 global fatal_error
1280 net = get_topogen().net
1281
1282 # Skip if previous fatal error condition is raised
1283 if fatal_error != "":
1284 pytest.skip(fatal_error)
1285
1286 thisDir = os.path.dirname(os.path.realpath(__file__))
1287
1288 print("\n\n** Verifying BGP IPv6")
1289 print("******************************************\n")
1290 diffresult = {}
1291 for i in range(1, 2):
1292 success = 0
1293 for refTableFile in glob.glob("%s/r%s/show_bgp_ipv6*.ref" % (thisDir, i)):
1294 if os.path.isfile(refTableFile):
1295 # Read expected result from file
1296 expected = open(refTableFile).read().rstrip()
1297 # Fix newlines (make them all the same)
1298 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1299
1300 # Actual output from router
1301 actual = (
1302 net["r%s" % i].cmd('vtysh -c "show bgp ipv6" 2> /dev/null').rstrip()
1303 )
1304 # Remove summary line (changed recently)
1305 actual = re.sub(r"Total number.*", "", actual)
1306 actual = re.sub(r"Displayed.*", "", actual)
1307 actual = actual.rstrip()
1308 # Fix newlines (make them all the same)
1309 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1310
1311 # Generate Diff
1312 diff = topotest.get_textdiff(
1313 actual,
1314 expected,
1315 title1="actual SHOW BGP IPv6",
1316 title2="expected SHOW BGP IPv6",
1317 )
1318
1319 # Empty string if it matches, otherwise diff contains unified diff
1320 if diff:
1321 diffresult[refTableFile] = diff
1322 else:
1323 success = 1
1324 print("template %s matched: r%s ok" % (refTableFile, i))
1325
1326 if not success:
1327 resultstr = "No template matched.\n"
1328 for f in diffresult.keys():
1329 resultstr += "template %s: r%s failed SHOW BGP IPv6 check:\n%s\n" % (
1330 f,
1331 i,
1332 diffresult[f],
1333 )
1334 raise AssertionError(
1335 "SHOW BGP IPv6 failed for router r%s:\n%s" % (i, resultstr)
1336 )
1337
1338 # Make sure that all daemons are running
1339 for i in range(1, 2):
1340 fatal_error = net["r%s" % i].checkRouterRunning()
1341 assert fatal_error == "", fatal_error
1342
1343
1344 def test_route_map():
1345 global fatal_error
1346 net = get_topogen().net
1347
1348 if fatal_error != "":
1349 pytest.skip(fatal_error)
1350
1351 thisDir = os.path.dirname(os.path.realpath(__file__))
1352
1353 print("\n\n** Verifying some basic routemap forward references\n")
1354 print("*******************************************************\n")
1355 failures = 0
1356 for i in range(1, 2):
1357 refroutemap = "%s/r%s/show_route_map.ref" % (thisDir, i)
1358 if os.path.isfile(refroutemap):
1359 expected = open(refroutemap).read().rstrip()
1360 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1361
1362 actual = (
1363 net["r%s" % i].cmd('vtysh -c "show route-map" 2> /dev/null').rstrip()
1364 )
1365 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1366
1367 diff = topotest.get_textdiff(
1368 actual,
1369 expected,
1370 title1="actual show route-map",
1371 title2="expected show route-map",
1372 )
1373
1374 if diff:
1375 sys.stderr.write(
1376 "r%s failed show route-map command Check:\n%s\n" % (i, diff)
1377 )
1378 failures += 1
1379 else:
1380 print("r%s ok" % i)
1381
1382 assert (
1383 failures == 0
1384 ), "Show route-map command failed for router r%s:\n%s" % (i, diff)
1385
1386
1387 def test_nexthop_groups_with_route_maps():
1388 global fatal_error
1389 net = get_topogen().net
1390
1391 # Skip if previous fatal error condition is raised
1392 if fatal_error != "":
1393 pytest.skip(fatal_error)
1394
1395 print("\n\n** Verifying Nexthop Groups With Route-Maps")
1396 print("******************************************\n")
1397
1398 ### Nexthop Group With Route-Map Tests
1399
1400 # Create a lib nexthop-group
1401 net["r1"].cmd(
1402 'vtysh -c "c t" -c "nexthop-group test" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"'
1403 )
1404
1405 ## Route-Map Proto Source
1406
1407 route_str = "2.2.2.1"
1408 src_str = "192.168.0.1"
1409
1410 net["r1"].cmd(
1411 'vtysh -c "c t" -c "route-map NH-SRC permit 111" -c "set src %s"' % src_str
1412 )
1413 net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NH-SRC"')
1414
1415 net["r1"].cmd('vtysh -c "sharp install routes %s nexthop-group test 1"' % route_str)
1416
1417 verify_route_nexthop_group("%s/32" % route_str)
1418
1419 # Only a valid test on linux using nexthop objects
1420 if sys.platform.startswith("linux"):
1421 output = net["r1"].cmd("ip route show %s/32" % route_str)
1422 match = re.search(r"src %s" % src_str, output)
1423 assert match is not None, "Route %s/32 not installed with src %s" % (
1424 route_str,
1425 src_str,
1426 )
1427
1428 # Remove NHG routes and route-map
1429 net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % route_str)
1430 net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NH-SRC"')
1431 net["r1"].cmd(
1432 'vtysh -c "c t" -c "no route-map NH-SRC permit 111" # -c "set src %s"' % src_str
1433 )
1434 net["r1"].cmd('vtysh -c "c t" -c "no route-map NH-SRC"')
1435
1436 ## Route-Map Deny/Permit with same nexthop group
1437
1438 permit_route_str = "3.3.3.1"
1439 deny_route_str = "3.3.3.2"
1440
1441 net["r1"].cmd(
1442 'vtysh -c "c t" -c "ip prefix-list NOPE seq 5 permit %s/32"' % permit_route_str
1443 )
1444 net["r1"].cmd(
1445 'vtysh -c "c t" -c "route-map NOPE permit 111" -c "match ip address prefix-list NOPE"'
1446 )
1447 net["r1"].cmd('vtysh -c "c t" -c "route-map NOPE deny 222"')
1448 net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NOPE"')
1449
1450 # This route should be permitted
1451 net["r1"].cmd(
1452 'vtysh -c "sharp install routes %s nexthop-group test 1"' % permit_route_str
1453 )
1454
1455 verify_route_nexthop_group("%s/32" % permit_route_str)
1456
1457 # This route should be denied
1458 net["r1"].cmd(
1459 'vtysh -c "sharp install routes %s nexthop-group test 1"' % deny_route_str
1460 )
1461
1462 nhg_id = route_get_nhg_id(deny_route_str)
1463 output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id)
1464
1465 match = re.search(r"Valid", output)
1466 assert match is None, "Nexthop Group ID=%d should not be marked Valid" % nhg_id
1467
1468 match = re.search(r"Installed", output)
1469 assert match is None, "Nexthop Group ID=%d should not be marked Installed" % nhg_id
1470
1471 # Remove NHG routes and route-map
1472 net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % permit_route_str)
1473 net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % deny_route_str)
1474 net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NOPE"')
1475 net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE permit 111"')
1476 net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE deny 222"')
1477 net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE"')
1478 net["r1"].cmd(
1479 'vtysh -c "c t" -c "no ip prefix-list NOPE seq 5 permit %s/32"'
1480 % permit_route_str
1481 )
1482
1483
1484 def test_nexthop_group_replace():
1485 global fatal_error
1486 net = get_topogen().net
1487
1488 # Skip if previous fatal error condition is raised
1489 if fatal_error != "":
1490 pytest.skip(fatal_error)
1491
1492 print("\n\n** Verifying Nexthop Groups")
1493 print("******************************************\n")
1494
1495 ### Nexthop Group Tests
1496
1497 ## 2-Way ECMP Directly Connected
1498
1499 net["r1"].cmd(
1500 'vtysh -c "c t" -c "nexthop-group replace" -c "nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.2 r1-eth2 onlink"'
1501 )
1502
1503 # Create with sharpd using nexthop-group
1504 net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.1 nexthop-group replace 1"')
1505
1506 verify_route_nexthop_group("3.3.3.1/32")
1507
1508 # Change the nexthop group
1509 net["r1"].cmd(
1510 'vtysh -c "c t" -c "nexthop-group replace" -c "no nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.3 r1-eth1 onlink" -c "nexthop 1.1.1.4 r1-eth4 onlink"'
1511 )
1512
1513 # Verify it updated. We can just check install and ecmp count here.
1514 verify_route_nexthop_group("3.3.3.1/32", False, 3)
1515
1516
1517 def test_mpls_interfaces():
1518 global fatal_error
1519 net = get_topogen().net
1520
1521 # Skip if previous fatal error condition is raised
1522 if fatal_error != "":
1523 pytest.skip(fatal_error)
1524
1525 # Skip if no LDP installed or old kernel
1526 if net["r1"].daemon_available("ldpd") == False:
1527 pytest.skip("No MPLS or kernel < 4.5")
1528
1529 thisDir = os.path.dirname(os.path.realpath(__file__))
1530
1531 print("\n\n** Verifying MPLS Interfaces")
1532 print("******************************************\n")
1533 failures = 0
1534 for i in range(1, 2):
1535 refTableFile = "%s/r%s/show_mpls_ldp_interface.ref" % (thisDir, i)
1536 if os.path.isfile(refTableFile):
1537 # Read expected result from file
1538 expected = open(refTableFile).read().rstrip()
1539 # Fix newlines (make them all the same)
1540 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1541
1542 # Actual output from router
1543 actual = (
1544 net["r%s" % i]
1545 .cmd('vtysh -c "show mpls ldp interface" 2> /dev/null')
1546 .rstrip()
1547 )
1548 # Mask out Timer in Uptime
1549 actual = re.sub(r" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual)
1550 # Fix newlines (make them all the same)
1551 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1552
1553 # Generate Diff
1554 diff = topotest.get_textdiff(
1555 actual,
1556 expected,
1557 title1="actual MPLS LDP interface status",
1558 title2="expected MPLS LDP interface status",
1559 )
1560
1561 # Empty string if it matches, otherwise diff contains unified diff
1562 if diff:
1563 sys.stderr.write(
1564 "r%s failed MPLS LDP Interface status Check:\n%s\n" % (i, diff)
1565 )
1566 failures += 1
1567 else:
1568 print("r%s ok" % i)
1569
1570 if failures > 0:
1571 fatal_error = "MPLS LDP Interface status failed"
1572
1573 assert (
1574 failures == 0
1575 ), "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff)
1576
1577 # Make sure that all daemons are running
1578 for i in range(1, 2):
1579 fatal_error = net["r%s" % i].checkRouterRunning()
1580 assert fatal_error == "", fatal_error
1581
1582
1583 def test_shutdown_check_stderr():
1584 global fatal_error
1585 net = get_topogen().net
1586
1587 # Skip if previous fatal error condition is raised
1588 if fatal_error != "":
1589 pytest.skip(fatal_error)
1590
1591 print("\n\n** Verifying unexpected STDERR output from daemons")
1592 print("******************************************\n")
1593
1594 if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
1595 print(
1596 "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
1597 )
1598 pytest.skip("Skipping test for Stderr output")
1599
1600 thisDir = os.path.dirname(os.path.realpath(__file__))
1601
1602 print("thisDir=" + thisDir)
1603
1604 net["r1"].stopRouter()
1605
1606 log = net["r1"].getStdErr("ripd")
1607 if log:
1608 print("\nRIPd StdErr Log:\n" + log)
1609 log = net["r1"].getStdErr("ripngd")
1610 if log:
1611 print("\nRIPngd StdErr Log:\n" + log)
1612 log = net["r1"].getStdErr("ospfd")
1613 if log:
1614 print("\nOSPFd StdErr Log:\n" + log)
1615 log = net["r1"].getStdErr("ospf6d")
1616 if log:
1617 print("\nOSPF6d StdErr Log:\n" + log)
1618 log = net["r1"].getStdErr("isisd")
1619 if log:
1620 print("\nISISd StdErr Log:\n" + log)
1621 log = net["r1"].getStdErr("bgpd")
1622 if log:
1623 print("\nBGPd StdErr Log:\n" + log)
1624
1625 log = net["r1"].getStdErr("nhrpd")
1626 if log:
1627 print("\nNHRPd StdErr Log:\n" + log)
1628
1629 log = net["r1"].getStdErr("pbrd")
1630 if log:
1631 print("\nPBRd StdErr Log:\n" + log)
1632
1633 log = net["r1"].getStdErr("babeld")
1634 if log:
1635 print("\nBABELd StdErr Log:\n" + log)
1636
1637 if net["r1"].daemon_available("ldpd"):
1638 log = net["r1"].getStdErr("ldpd")
1639 if log:
1640 print("\nLDPd StdErr Log:\n" + log)
1641 log = net["r1"].getStdErr("zebra")
1642 if log:
1643 print("\nZebra StdErr Log:\n" + log)
1644
1645
1646 def test_shutdown_check_memleak():
1647 global fatal_error
1648 net = get_topogen().net
1649
1650 # Skip if previous fatal error condition is raised
1651 if fatal_error != "":
1652 pytest.skip(fatal_error)
1653
1654 if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None:
1655 print(
1656 "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n"
1657 )
1658 pytest.skip("Skipping test for memory leaks")
1659
1660 thisDir = os.path.dirname(os.path.realpath(__file__))
1661
1662 for i in range(1, 2):
1663 net["r%s" % i].stopRouter()
1664 net["r%s" % i].report_memory_leaks(
1665 os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__)
1666 )
1667
1668
1669 if __name__ == "__main__":
1670 # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
1671 # retval = pytest.main(["-s", "--tb=no"])
1672 retval = pytest.main(["-s"])
1673 sys.exit(retval)