]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/all_protocol_startup/test_all_protocol_startup.py
Merge pull request #9958 from donaldsharp/all_protocol_nhg_replace
[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 sleep(5)
572
573 net["r1"].cmd(
574 'vtysh -c "sharp install routes 6.6.6.3 nexthop-group infinite-recursive 1"'
575 )
576 sleep(5)
577
578 net["r1"].cmd(
579 'vtysh -c "sharp install routes 6.6.6.2 nexthop-group infinite-recursive 1"'
580 )
581 sleep(5)
582
583 net["r1"].cmd(
584 'vtysh -c "sharp install routes 6.6.6.1 nexthop-group infinite-recursive 1"'
585 )
586
587 # Get routes and test if has too many (duplicate) nexthops
588 count = 0
589 dups = []
590 nhg_id = route_get_nhg_id("6.6.6.1/32")
591 while (len(dups) != 3) and count < 10:
592 output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id)
593
594 dups = re.findall(r"(via 1\.1\.1\.1)", output)
595 if len(dups) != 3:
596 count += 1
597 sleep(1)
598
599 # Should find 3, itself is inactive
600 assert len(dups) == 3, (
601 "Route 6.6.6.1/32 with Nexthop Group ID=%d has wrong number of resolved nexthops"
602 % nhg_id
603 )
604
605 ## Remove all NHG routes
606
607 net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.1 1"')
608 net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.2 1"')
609 net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.1 1"')
610 net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.2 1"')
611 net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.1 1"')
612 net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.2 1"')
613 net["r1"].cmd('vtysh -c "sharp remove routes 5.5.5.1 1"')
614 net["r1"].cmd('vtysh -c "sharp remove routes 6.6.6.1 4"')
615 net["r1"].cmd('vtysh -c "c t" -c "no ip route 6.6.6.0/24 1.1.1.1"')
616
617
618 def test_rip_status():
619 global fatal_error
620 net = get_topogen().net
621
622 # Skip if previous fatal error condition is raised
623 if fatal_error != "":
624 pytest.skip(fatal_error)
625
626 thisDir = os.path.dirname(os.path.realpath(__file__))
627
628 print("\n\n** Verifying RIP status")
629 print("******************************************\n")
630 failures = 0
631 for i in range(1, 2):
632 refTableFile = "%s/r%s/rip_status.ref" % (thisDir, i)
633 if os.path.isfile(refTableFile):
634 # Read expected result from file
635 expected = open(refTableFile).read().rstrip()
636 # Fix newlines (make them all the same)
637 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
638
639 # Actual output from router
640 actual = (
641 net["r%s" % i]
642 .cmd('vtysh -c "show ip rip status" 2> /dev/null')
643 .rstrip()
644 )
645 # Drop time in next due
646 actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
647 # Drop time in last update
648 actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
649 # Fix newlines (make them all the same)
650 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
651
652 # Generate Diff
653 diff = topotest.get_textdiff(
654 actual,
655 expected,
656 title1="actual IP RIP status",
657 title2="expected IP RIP status",
658 )
659
660 # Empty string if it matches, otherwise diff contains unified diff
661 if diff:
662 sys.stderr.write("r%s failed IP RIP status check:\n%s\n" % (i, diff))
663 failures += 1
664 else:
665 print("r%s ok" % i)
666
667 assert failures == 0, "IP RIP status failed for router r%s:\n%s" % (i, diff)
668
669 # Make sure that all daemons are running
670 for i in range(1, 2):
671 fatal_error = net["r%s" % i].checkRouterRunning()
672 assert fatal_error == "", fatal_error
673
674
675 def test_ripng_status():
676 global fatal_error
677 net = get_topogen().net
678
679 # Skip if previous fatal error condition is raised
680 if fatal_error != "":
681 pytest.skip(fatal_error)
682
683 thisDir = os.path.dirname(os.path.realpath(__file__))
684
685 print("\n\n** Verifying RIPng status")
686 print("******************************************\n")
687 failures = 0
688 for i in range(1, 2):
689 refTableFile = "%s/r%s/ripng_status.ref" % (thisDir, i)
690 if os.path.isfile(refTableFile):
691 # Read expected result from file
692 expected = open(refTableFile).read().rstrip()
693 # Fix newlines (make them all the same)
694 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
695
696 # Actual output from router
697 actual = (
698 net["r%s" % i]
699 .cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null')
700 .rstrip()
701 )
702 # Mask out Link-Local mac address portion. They are random...
703 actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual)
704 # Drop time in next due
705 actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
706 # Drop time in last update
707 actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
708 # Fix newlines (make them all the same)
709 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
710
711 # Generate Diff
712 diff = topotest.get_textdiff(
713 actual,
714 expected,
715 title1="actual IPv6 RIPng status",
716 title2="expected IPv6 RIPng status",
717 )
718
719 # Empty string if it matches, otherwise diff contains unified diff
720 if diff:
721 sys.stderr.write(
722 "r%s failed IPv6 RIPng status check:\n%s\n" % (i, diff)
723 )
724 failures += 1
725 else:
726 print("r%s ok" % i)
727
728 assert failures == 0, "IPv6 RIPng status failed for router r%s:\n%s" % (
729 i,
730 diff,
731 )
732
733 # Make sure that all daemons are running
734 for i in range(1, 2):
735 fatal_error = net["r%s" % i].checkRouterRunning()
736 assert fatal_error == "", fatal_error
737
738
739 def test_ospfv2_interfaces():
740 global fatal_error
741 net = get_topogen().net
742
743 # Skip if previous fatal error condition is raised
744 if fatal_error != "":
745 pytest.skip(fatal_error)
746
747 thisDir = os.path.dirname(os.path.realpath(__file__))
748
749 print("\n\n** Verifying OSPFv2 interfaces")
750 print("******************************************\n")
751 failures = 0
752 for i in range(1, 2):
753 refTableFile = "%s/r%s/show_ip_ospf_interface.ref" % (thisDir, i)
754 if os.path.isfile(refTableFile):
755 # Read expected result from file
756 expected = open(refTableFile).read().rstrip()
757 # Fix newlines (make them all the same)
758 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
759
760 # Actual output from router
761 actual = (
762 net["r%s" % i]
763 .cmd('vtysh -c "show ip ospf interface" 2> /dev/null')
764 .rstrip()
765 )
766 # Mask out Bandwidth portion. They may change..
767 actual = re.sub(r"BW [0-9]+ Mbit", "BW XX Mbit", actual)
768 actual = re.sub(r"ifindex [0-9]+", "ifindex X", actual)
769
770 # Drop time in next due
771 actual = re.sub(r"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual)
772 actual = re.sub(
773 r"Hello due in [0-9\.]+ usecs", "Hello due in XX.XXXs", actual
774 )
775 # Fix 'MTU mismatch detection: enabled' vs 'MTU mismatch detection:enabled' - accept both
776 actual = re.sub(
777 r"MTU mismatch detection:([a-z]+.*)",
778 r"MTU mismatch detection: \1",
779 actual,
780 )
781 # Fix newlines (make them all the same)
782 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
783
784 # Generate Diff
785 diff = topotest.get_textdiff(
786 actual,
787 expected,
788 title1="actual SHOW IP OSPF INTERFACE",
789 title2="expected SHOW IP OSPF INTERFACE",
790 )
791
792 # Empty string if it matches, otherwise diff contains unified diff
793 if diff:
794 sys.stderr.write(
795 "r%s failed SHOW IP OSPF INTERFACE check:\n%s\n" % (i, diff)
796 )
797 failures += 1
798 else:
799 print("r%s ok" % i)
800
801 # Ignoring the issue if told to ignore (ie not yet fixed)
802 if failures != 0:
803 if os.environ.get("bamboo_TOPOTESTS_ISSUE_348") == "IGNORE":
804 sys.stderr.write(
805 "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348\n"
806 )
807 pytest.skip(
808 "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348"
809 )
810
811 assert (
812 failures == 0
813 ), "SHOW IP OSPF INTERFACE failed for router r%s:\n%s" % (i, diff)
814
815 # Make sure that all daemons are running
816 for i in range(1, 2):
817 fatal_error = net["r%s" % i].checkRouterRunning()
818 assert fatal_error == "", fatal_error
819
820
821 def test_isis_interfaces():
822 global fatal_error
823 net = get_topogen().net
824
825 # Skip if previous fatal error condition is raised
826 if fatal_error != "":
827 pytest.skip(fatal_error)
828
829 thisDir = os.path.dirname(os.path.realpath(__file__))
830
831 print("\n\n** Verifying ISIS interfaces")
832 print("******************************************\n")
833 failures = 0
834 for i in range(1, 2):
835 refTableFile = "%s/r%s/show_isis_interface_detail.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 = (
844 net["r%s" % i]
845 .cmd('vtysh -c "show isis interface detail" 2> /dev/null')
846 .rstrip()
847 )
848 # Mask out Link-Local mac address portion. They are random...
849 actual = re.sub(r"fe80::[0-9a-f:]+", "fe80::XXXX:XXXX:XXXX:XXXX", actual)
850 # Mask out SNPA mac address portion. They are random...
851 actual = re.sub(r"SNPA: [0-9a-f\.]+", "SNPA: XXXX.XXXX.XXXX", actual)
852 # Mask out Circuit ID number
853 actual = re.sub(r"Circuit Id: 0x[0-9a-f]+", "Circuit Id: 0xXX", actual)
854 # Fix newlines (make them all the same)
855 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
856
857 # Generate Diff
858 diff = topotest.get_textdiff(
859 actual,
860 expected,
861 title1="actual SHOW ISIS INTERFACE DETAIL",
862 title2="expected SHOW ISIS OSPF6 INTERFACE DETAIL",
863 )
864
865 # Empty string if it matches, otherwise diff contains unified diff
866 if diff:
867 sys.stderr.write(
868 "r%s failed SHOW ISIS INTERFACE DETAIL check:\n%s\n" % (i, diff)
869 )
870 failures += 1
871 else:
872 print("r%s ok" % i)
873
874 assert (
875 failures == 0
876 ), "SHOW ISIS INTERFACE DETAIL failed for router r%s:\n%s" % (i, diff)
877
878 # Make sure that all daemons are running
879 for i in range(1, 2):
880 fatal_error = net["r%s" % i].checkRouterRunning()
881 assert fatal_error == "", fatal_error
882
883
884 def test_bgp_summary():
885 global fatal_error
886 net = get_topogen().net
887
888 # Skip if previous fatal error condition is raised
889 if fatal_error != "":
890 pytest.skip(fatal_error)
891
892 thisDir = os.path.dirname(os.path.realpath(__file__))
893
894 print("\n\n** Verifying BGP Summary")
895 print("******************************************\n")
896 failures = 0
897 for i in range(1, 2):
898 refTableFile = "%s/r%s/show_ip_bgp_summary.ref" % (thisDir, i)
899 if os.path.isfile(refTableFile):
900 # Read expected result from file
901 expected_original = open(refTableFile).read().rstrip()
902
903 for arguments in [
904 "",
905 "remote-as internal",
906 "remote-as external",
907 "remote-as 100",
908 "remote-as 123",
909 "neighbor 192.168.7.10",
910 "neighbor 192.168.7.10",
911 "neighbor fc00:0:0:8::1000",
912 "neighbor 10.0.0.1",
913 "terse",
914 "remote-as internal terse",
915 "remote-as external terse",
916 "remote-as 100 terse",
917 "remote-as 123 terse",
918 "neighbor 192.168.7.10 terse",
919 "neighbor 192.168.7.10 terse",
920 "neighbor fc00:0:0:8::1000 terse",
921 "neighbor 10.0.0.1 terse",
922 ]:
923 # Actual output from router
924 actual = (
925 net["r%s" % i]
926 .cmd(
927 'vtysh -c "show ip bgp summary ' + arguments + '" 2> /dev/null'
928 )
929 .rstrip()
930 )
931
932 # Mask out "using XXiXX bytes" portion. They are random...
933 actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual)
934 # Mask out "using XiXXX KiB" portion. They are random...
935 actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual)
936
937 # Remove extra summaries which exist with newer versions
938
939 # Remove summary lines (changed recently)
940 actual = re.sub(r"Total number.*", "", actual)
941 actual = re.sub(r"Displayed.*", "", actual)
942 # Remove IPv4 Unicast Summary (Title only)
943 actual = re.sub(r"IPv4 Unicast Summary \(VRF default\):", "", actual)
944 # Remove IPv4 Multicast Summary (all of it)
945 actual = re.sub(r"IPv4 Multicast Summary \(VRF default\):", "", actual)
946 actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual)
947 # Remove IPv4 VPN Summary (all of it)
948 actual = re.sub(r"IPv4 VPN Summary \(VRF default\):", "", actual)
949 actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual)
950 # Remove IPv4 Encap Summary (all of it)
951 actual = re.sub(r"IPv4 Encap Summary \(VRF default\):", "", actual)
952 actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual)
953 # Remove Unknown Summary (all of it)
954 actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual)
955 actual = re.sub(r"No Unknown neighbor is configured", "", actual)
956 # Make Connect/Active/Idle the same (change them all to Active)
957 actual = re.sub(r" Connect ", " Active ", actual)
958 actual = re.sub(r" Idle ", " Active ", actual)
959
960 actual = re.sub(
961 r"IPv4 labeled-unicast Summary \(VRF default\):", "", actual
962 )
963 actual = re.sub(
964 r"No IPv4 labeled-unicast neighbor is configured", "", actual
965 )
966
967 expected = expected_original
968 # apply argumentss on expected output
969 if "internal" in arguments or "remote-as 100" in arguments:
970 expected = re.sub(r".+\s+200\s+.+", "", expected)
971 elif "external" in arguments:
972 expected = re.sub(r".+\s+100\s+.+Active.+", "", expected)
973 elif "remote-as 123" in arguments:
974 expected = re.sub(
975 r"(192.168.7.(1|2)0|fc00:0:0:8::(1|2)000).+Active.+",
976 "",
977 expected,
978 )
979 expected = re.sub(r"\nNeighbor.+Desc", "", expected)
980 expected = expected + "% No matching neighbor\n"
981 elif "192.168.7.10" in arguments:
982 expected = re.sub(
983 r"(192.168.7.20|fc00:0:0:8::(1|2)000).+Active.+", "", expected
984 )
985 elif "fc00:0:0:8::1000" in arguments:
986 expected = re.sub(
987 r"(192.168.7.(1|2)0|fc00:0:0:8::2000).+Active.+", "", expected
988 )
989 elif "10.0.0.1" in arguments:
990 expected = "No such neighbor in this view/vrf"
991
992 if "terse" in arguments:
993 expected = re.sub(r"BGP table version .+", "", expected)
994 expected = re.sub(r"RIB entries .+", "", expected)
995 expected = re.sub(r"Peers [0-9]+, using .+", "", expected)
996
997 # Strip empty lines
998 actual = actual.lstrip().rstrip()
999 expected = expected.lstrip().rstrip()
1000 actual = re.sub(r"\n+", "\n", actual)
1001 expected = re.sub(r"\n+", "\n", expected)
1002
1003 # reapply initial formatting
1004 if "terse" in arguments:
1005 actual = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", actual)
1006 expected = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", expected)
1007 else:
1008 actual = re.sub(r"KiB of memory\n", "KiB of memory\n\n", actual)
1009 expected = re.sub(r"KiB of memory\n", "KiB of memory\n\n", expected)
1010
1011 # realign expected neighbor columns if needed
1012 try:
1013 idx_actual = (
1014 re.search(r"(Neighbor\s+V\s+)", actual).group(1).find("V")
1015 )
1016 idx_expected = (
1017 re.search(r"(Neighbor\s+V\s+)", expected).group(1).find("V")
1018 )
1019 idx_diff = idx_expected - idx_actual
1020 if idx_diff > 0:
1021 # Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
1022 expected = re.sub(" " * idx_diff + "V ", "V ", expected)
1023 # 192.168.7.10 4 100 0 0 0 0 0 never Active
1024 expected = re.sub(" " * idx_diff + "4 ", "4 ", expected)
1025 except AttributeError:
1026 pass
1027
1028 # Fix newlines (make them all the same)
1029 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1030 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1031
1032 # Generate Diff
1033 diff = topotest.get_textdiff(
1034 actual,
1035 expected,
1036 title1="actual SHOW IP BGP SUMMARY " + arguments.upper(),
1037 title2="expected SHOW IP BGP SUMMARY " + arguments.upper(),
1038 )
1039
1040 # Empty string if it matches, otherwise diff contains unified diff
1041 if diff:
1042 sys.stderr.write(
1043 "r%s failed SHOW IP BGP SUMMARY check:\n%s\n" % (i, diff)
1044 )
1045 failures += 1
1046 else:
1047 print("r%s ok" % i)
1048
1049 assert (
1050 failures == 0
1051 ), "SHOW IP BGP SUMMARY failed for router r%s:\n%s" % (
1052 i,
1053 diff,
1054 )
1055
1056 # Make sure that all daemons are running
1057 for i in range(1, 2):
1058 fatal_error = net["r%s" % i].checkRouterRunning()
1059 assert fatal_error == "", fatal_error
1060
1061
1062 def test_bgp_ipv6_summary():
1063 global fatal_error
1064 net = get_topogen().net
1065
1066 # Skip if previous fatal error condition is raised
1067 if fatal_error != "":
1068 pytest.skip(fatal_error)
1069
1070 thisDir = os.path.dirname(os.path.realpath(__file__))
1071
1072 print("\n\n** Verifying BGP IPv6 Summary")
1073 print("******************************************\n")
1074 failures = 0
1075 for i in range(1, 2):
1076 refTableFile = "%s/r%s/show_bgp_ipv6_summary.ref" % (thisDir, i)
1077 if os.path.isfile(refTableFile):
1078 # Read expected result from file
1079 expected = open(refTableFile).read().rstrip()
1080 # Fix newlines (make them all the same)
1081 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1082
1083 # Actual output from router
1084 actual = (
1085 net["r%s" % i]
1086 .cmd('vtysh -c "show bgp ipv6 summary" 2> /dev/null')
1087 .rstrip()
1088 )
1089 # Mask out "using XXiXX bytes" portion. They are random...
1090 actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual)
1091 # Mask out "using XiXXX KiB" portion. They are random...
1092 actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual)
1093 #
1094 # Remove extra summaries which exist with newer versions
1095 #
1096 # Remove summary lines (changed recently)
1097 actual = re.sub(r"Total number.*", "", actual)
1098 actual = re.sub(r"Displayed.*", "", actual)
1099 # Remove IPv4 Unicast Summary (Title only)
1100 actual = re.sub(r"IPv6 Unicast Summary \(VRF default\):", "", actual)
1101 # Remove IPv4 Multicast Summary (all of it)
1102 actual = re.sub(r"IPv6 Multicast Summary \(VRF default\):", "", actual)
1103 actual = re.sub(r"No IPv6 Multicast neighbor is configured", "", actual)
1104 # Remove IPv4 VPN Summary (all of it)
1105 actual = re.sub(r"IPv6 VPN Summary \(VRF default\):", "", actual)
1106 actual = re.sub(r"No IPv6 VPN neighbor is configured", "", actual)
1107 # Remove IPv4 Encap Summary (all of it)
1108 actual = re.sub(r"IPv6 Encap Summary \(VRF default\):", "", actual)
1109 actual = re.sub(r"No IPv6 Encap neighbor is configured", "", actual)
1110 # Remove Unknown Summary (all of it)
1111 actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual)
1112 actual = re.sub(r"No Unknown neighbor is configured", "", actual)
1113 # Make Connect/Active/Idle the same (change them all to Active)
1114 actual = re.sub(r" Connect ", " Active ", actual)
1115 actual = re.sub(r" Idle ", " Active ", actual)
1116
1117 # Remove Labeled Unicast Summary (all of it)
1118 actual = re.sub(
1119 r"IPv6 labeled-unicast Summary \(VRF default\):", "", actual
1120 )
1121 actual = re.sub(
1122 r"No IPv6 labeled-unicast neighbor is configured", "", actual
1123 )
1124
1125 # Strip empty lines
1126 actual = actual.lstrip()
1127 actual = actual.rstrip()
1128 #
1129 # Fix newlines (make them all the same)
1130 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1131
1132 # Generate Diff
1133 diff = topotest.get_textdiff(
1134 actual,
1135 expected,
1136 title1="actual SHOW BGP IPv6 SUMMARY",
1137 title2="expected SHOW BGP IPv6 SUMMARY",
1138 )
1139
1140 # Empty string if it matches, otherwise diff contains unified diff
1141 if diff:
1142 sys.stderr.write(
1143 "r%s failed SHOW BGP IPv6 SUMMARY check:\n%s\n" % (i, diff)
1144 )
1145 failures += 1
1146 else:
1147 print("r%s ok" % i)
1148
1149 assert failures == 0, "SHOW BGP IPv6 SUMMARY failed for router r%s:\n%s" % (
1150 i,
1151 diff,
1152 )
1153
1154 # Make sure that all daemons are running
1155 for i in range(1, 2):
1156 fatal_error = net["r%s" % i].checkRouterRunning()
1157 assert fatal_error == "", fatal_error
1158
1159
1160 def test_nht():
1161 global fatal_error
1162 net = get_topogen().net
1163
1164 # Skip if previous fatal error condition is raised
1165 if fatal_error != "":
1166 pytest.skip(fatal_error)
1167
1168 print("\n\n**** Test that nexthop tracking is at least nominally working ****\n")
1169
1170 thisDir = os.path.dirname(os.path.realpath(__file__))
1171
1172 for i in range(1, 2):
1173 nhtFile = "%s/r%s/ip_nht.ref" % (thisDir, i)
1174 expected = open(nhtFile).read().rstrip()
1175 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1176
1177 actual = net["r%s" % i].cmd('vtysh -c "show ip nht" 2> /dev/null').rstrip()
1178 actual = re.sub(r"fd [0-9]+", "fd XX", actual)
1179 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1180
1181 diff = topotest.get_textdiff(
1182 actual,
1183 expected,
1184 title1="Actual `show ip nht`",
1185 title2="Expected `show ip nht`",
1186 )
1187
1188 if diff:
1189 assert 0, "r%s failed ip nht check:\n%s\n" % (i, diff)
1190 else:
1191 print("show ip nht is ok\n")
1192
1193 nhtFile = "%s/r%s/ipv6_nht.ref" % (thisDir, i)
1194 expected = open(nhtFile).read().rstrip()
1195 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1196
1197 actual = net["r%s" % i].cmd('vtysh -c "show ipv6 nht" 2> /dev/null').rstrip()
1198 actual = re.sub(r"fd [0-9]+", "fd XX", actual)
1199 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1200
1201 diff = topotest.get_textdiff(
1202 actual,
1203 expected,
1204 title1="Actual `show ip nht`",
1205 title2="Expected `show ip nht`",
1206 )
1207
1208 if diff:
1209 assert 0, "r%s failed ipv6 nht check:\n%s\n" % (i, diff)
1210 else:
1211 print("show ipv6 nht is ok\n")
1212
1213
1214 def test_bgp_ipv4():
1215 global fatal_error
1216 net = get_topogen().net
1217
1218 # Skip if previous fatal error condition is raised
1219 if fatal_error != "":
1220 pytest.skip(fatal_error)
1221
1222 thisDir = os.path.dirname(os.path.realpath(__file__))
1223
1224 print("\n\n** Verifying BGP IPv4")
1225 print("******************************************\n")
1226 diffresult = {}
1227 for i in range(1, 2):
1228 success = 0
1229 for refTableFile in glob.glob("%s/r%s/show_bgp_ipv4*.ref" % (thisDir, i)):
1230 if os.path.isfile(refTableFile):
1231 # Read expected result from file
1232 expected = open(refTableFile).read().rstrip()
1233 # Fix newlines (make them all the same)
1234 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1235
1236 # Actual output from router
1237 actual = (
1238 net["r%s" % i].cmd('vtysh -c "show bgp ipv4" 2> /dev/null').rstrip()
1239 )
1240 # Remove summary line (changed recently)
1241 actual = re.sub(r"Total number.*", "", actual)
1242 actual = re.sub(r"Displayed.*", "", actual)
1243 actual = actual.rstrip()
1244 # Fix newlines (make them all the same)
1245 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1246
1247 # Generate Diff
1248 diff = topotest.get_textdiff(
1249 actual,
1250 expected,
1251 title1="actual SHOW BGP IPv4",
1252 title2="expected SHOW BGP IPv4",
1253 )
1254
1255 # Empty string if it matches, otherwise diff contains unified diff
1256 if diff:
1257 diffresult[refTableFile] = diff
1258 else:
1259 success = 1
1260 print("template %s matched: r%s ok" % (refTableFile, i))
1261 break
1262
1263 if not success:
1264 resultstr = "No template matched.\n"
1265 for f in diffresult.keys():
1266 resultstr += "template %s: r%s failed SHOW BGP IPv4 check:\n%s\n" % (
1267 f,
1268 i,
1269 diffresult[f],
1270 )
1271 raise AssertionError(
1272 "SHOW BGP IPv4 failed for router r%s:\n%s" % (i, resultstr)
1273 )
1274
1275 # Make sure that all daemons are running
1276 for i in range(1, 2):
1277 fatal_error = net["r%s" % i].checkRouterRunning()
1278 assert fatal_error == "", fatal_error
1279
1280
1281 def test_bgp_ipv6():
1282 global fatal_error
1283 net = get_topogen().net
1284
1285 # Skip if previous fatal error condition is raised
1286 if fatal_error != "":
1287 pytest.skip(fatal_error)
1288
1289 thisDir = os.path.dirname(os.path.realpath(__file__))
1290
1291 print("\n\n** Verifying BGP IPv6")
1292 print("******************************************\n")
1293 diffresult = {}
1294 for i in range(1, 2):
1295 success = 0
1296 for refTableFile in glob.glob("%s/r%s/show_bgp_ipv6*.ref" % (thisDir, i)):
1297 if os.path.isfile(refTableFile):
1298 # Read expected result from file
1299 expected = open(refTableFile).read().rstrip()
1300 # Fix newlines (make them all the same)
1301 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1302
1303 # Actual output from router
1304 actual = (
1305 net["r%s" % i].cmd('vtysh -c "show bgp ipv6" 2> /dev/null').rstrip()
1306 )
1307 # Remove summary line (changed recently)
1308 actual = re.sub(r"Total number.*", "", actual)
1309 actual = re.sub(r"Displayed.*", "", actual)
1310 actual = actual.rstrip()
1311 # Fix newlines (make them all the same)
1312 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1313
1314 # Generate Diff
1315 diff = topotest.get_textdiff(
1316 actual,
1317 expected,
1318 title1="actual SHOW BGP IPv6",
1319 title2="expected SHOW BGP IPv6",
1320 )
1321
1322 # Empty string if it matches, otherwise diff contains unified diff
1323 if diff:
1324 diffresult[refTableFile] = diff
1325 else:
1326 success = 1
1327 print("template %s matched: r%s ok" % (refTableFile, i))
1328
1329 if not success:
1330 resultstr = "No template matched.\n"
1331 for f in diffresult.keys():
1332 resultstr += "template %s: r%s failed SHOW BGP IPv6 check:\n%s\n" % (
1333 f,
1334 i,
1335 diffresult[f],
1336 )
1337 raise AssertionError(
1338 "SHOW BGP IPv6 failed for router r%s:\n%s" % (i, resultstr)
1339 )
1340
1341 # Make sure that all daemons are running
1342 for i in range(1, 2):
1343 fatal_error = net["r%s" % i].checkRouterRunning()
1344 assert fatal_error == "", fatal_error
1345
1346
1347 def test_route_map():
1348 global fatal_error
1349 net = get_topogen().net
1350
1351 if fatal_error != "":
1352 pytest.skip(fatal_error)
1353
1354 thisDir = os.path.dirname(os.path.realpath(__file__))
1355
1356 print("\n\n** Verifying some basic routemap forward references\n")
1357 print("*******************************************************\n")
1358 failures = 0
1359 for i in range(1, 2):
1360 refroutemap = "%s/r%s/show_route_map.ref" % (thisDir, i)
1361 if os.path.isfile(refroutemap):
1362 expected = open(refroutemap).read().rstrip()
1363 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1364
1365 actual = (
1366 net["r%s" % i].cmd('vtysh -c "show route-map" 2> /dev/null').rstrip()
1367 )
1368 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1369
1370 diff = topotest.get_textdiff(
1371 actual,
1372 expected,
1373 title1="actual show route-map",
1374 title2="expected show route-map",
1375 )
1376
1377 if diff:
1378 sys.stderr.write(
1379 "r%s failed show route-map command Check:\n%s\n" % (i, diff)
1380 )
1381 failures += 1
1382 else:
1383 print("r%s ok" % i)
1384
1385 assert (
1386 failures == 0
1387 ), "Show route-map command failed for router r%s:\n%s" % (i, diff)
1388
1389
1390 def test_nexthop_groups_with_route_maps():
1391 global fatal_error
1392 net = get_topogen().net
1393
1394 # Skip if previous fatal error condition is raised
1395 if fatal_error != "":
1396 pytest.skip(fatal_error)
1397
1398 print("\n\n** Verifying Nexthop Groups With Route-Maps")
1399 print("******************************************\n")
1400
1401 ### Nexthop Group With Route-Map Tests
1402
1403 # Create a lib nexthop-group
1404 net["r1"].cmd(
1405 'vtysh -c "c t" -c "nexthop-group test" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"'
1406 )
1407
1408 ## Route-Map Proto Source
1409
1410 route_str = "2.2.2.1"
1411 src_str = "192.168.0.1"
1412
1413 net["r1"].cmd(
1414 'vtysh -c "c t" -c "route-map NH-SRC permit 111" -c "set src %s"' % src_str
1415 )
1416 net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NH-SRC"')
1417
1418 net["r1"].cmd('vtysh -c "sharp install routes %s nexthop-group test 1"' % route_str)
1419
1420 verify_route_nexthop_group("%s/32" % route_str)
1421
1422 # Only a valid test on linux using nexthop objects
1423 if sys.platform.startswith("linux"):
1424 output = net["r1"].cmd("ip route show %s/32" % route_str)
1425 match = re.search(r"src %s" % src_str, output)
1426 assert match is not None, "Route %s/32 not installed with src %s" % (
1427 route_str,
1428 src_str,
1429 )
1430
1431 # Remove NHG routes and route-map
1432 net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % route_str)
1433 net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NH-SRC"')
1434 net["r1"].cmd(
1435 'vtysh -c "c t" -c "no route-map NH-SRC permit 111" # -c "set src %s"' % src_str
1436 )
1437 net["r1"].cmd('vtysh -c "c t" -c "no route-map NH-SRC"')
1438
1439 ## Route-Map Deny/Permit with same nexthop group
1440
1441 permit_route_str = "3.3.3.1"
1442 deny_route_str = "3.3.3.2"
1443
1444 net["r1"].cmd(
1445 'vtysh -c "c t" -c "ip prefix-list NOPE seq 5 permit %s/32"' % permit_route_str
1446 )
1447 net["r1"].cmd(
1448 'vtysh -c "c t" -c "route-map NOPE permit 111" -c "match ip address prefix-list NOPE"'
1449 )
1450 net["r1"].cmd('vtysh -c "c t" -c "route-map NOPE deny 222"')
1451 net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NOPE"')
1452
1453 # This route should be permitted
1454 net["r1"].cmd(
1455 'vtysh -c "sharp install routes %s nexthop-group test 1"' % permit_route_str
1456 )
1457
1458 verify_route_nexthop_group("%s/32" % permit_route_str)
1459
1460 # This route should be denied
1461 net["r1"].cmd(
1462 'vtysh -c "sharp install routes %s nexthop-group test 1"' % deny_route_str
1463 )
1464
1465 nhg_id = route_get_nhg_id(deny_route_str)
1466 output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id)
1467
1468 match = re.search(r"Valid", output)
1469 assert match is None, "Nexthop Group ID=%d should not be marked Valid" % nhg_id
1470
1471 match = re.search(r"Installed", output)
1472 assert match is None, "Nexthop Group ID=%d should not be marked Installed" % nhg_id
1473
1474 # Remove NHG routes and route-map
1475 net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % permit_route_str)
1476 net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % deny_route_str)
1477 net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NOPE"')
1478 net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE permit 111"')
1479 net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE deny 222"')
1480 net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE"')
1481 net["r1"].cmd(
1482 'vtysh -c "c t" -c "no ip prefix-list NOPE seq 5 permit %s/32"'
1483 % permit_route_str
1484 )
1485
1486
1487 def test_nexthop_group_replace():
1488 global fatal_error
1489 net = get_topogen().net
1490
1491 # Skip if previous fatal error condition is raised
1492 if fatal_error != "":
1493 pytest.skip(fatal_error)
1494
1495 print("\n\n** Verifying Nexthop Groups")
1496 print("******************************************\n")
1497
1498 ### Nexthop Group Tests
1499
1500 ## 2-Way ECMP Directly Connected
1501
1502 net["r1"].cmd(
1503 '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"'
1504 )
1505
1506 # At the moment there is absolutely no real easy way to query sharpd
1507 # for the nexthop group actually installed. If it is not installed
1508 # sharpd will just transmit the nexthops down instead of the nexthop
1509 # group id. Leading to a situation where the replace is not actually
1510 # being tested. So let's just wait some time here because this
1511 # is hard and this test fails all the time
1512 sleep(5)
1513
1514 # Create with sharpd using nexthop-group
1515 net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.1 nexthop-group replace 1"')
1516
1517 verify_route_nexthop_group("3.3.3.1/32")
1518
1519 # Change the nexthop group
1520 net["r1"].cmd(
1521 '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"'
1522 )
1523
1524 # Verify it updated. We can just check install and ecmp count here.
1525 verify_route_nexthop_group("3.3.3.1/32", False, 3)
1526
1527
1528 def test_mpls_interfaces():
1529 global fatal_error
1530 net = get_topogen().net
1531
1532 # Skip if previous fatal error condition is raised
1533 if fatal_error != "":
1534 pytest.skip(fatal_error)
1535
1536 # Skip if no LDP installed or old kernel
1537 if net["r1"].daemon_available("ldpd") == False:
1538 pytest.skip("No MPLS or kernel < 4.5")
1539
1540 thisDir = os.path.dirname(os.path.realpath(__file__))
1541
1542 print("\n\n** Verifying MPLS Interfaces")
1543 print("******************************************\n")
1544 failures = 0
1545 for i in range(1, 2):
1546 refTableFile = "%s/r%s/show_mpls_ldp_interface.ref" % (thisDir, i)
1547 if os.path.isfile(refTableFile):
1548 # Read expected result from file
1549 expected = open(refTableFile).read().rstrip()
1550 # Fix newlines (make them all the same)
1551 expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
1552
1553 # Actual output from router
1554 actual = (
1555 net["r%s" % i]
1556 .cmd('vtysh -c "show mpls ldp interface" 2> /dev/null')
1557 .rstrip()
1558 )
1559 # Mask out Timer in Uptime
1560 actual = re.sub(r" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual)
1561 # Fix newlines (make them all the same)
1562 actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
1563
1564 # Generate Diff
1565 diff = topotest.get_textdiff(
1566 actual,
1567 expected,
1568 title1="actual MPLS LDP interface status",
1569 title2="expected MPLS LDP interface status",
1570 )
1571
1572 # Empty string if it matches, otherwise diff contains unified diff
1573 if diff:
1574 sys.stderr.write(
1575 "r%s failed MPLS LDP Interface status Check:\n%s\n" % (i, diff)
1576 )
1577 failures += 1
1578 else:
1579 print("r%s ok" % i)
1580
1581 if failures > 0:
1582 fatal_error = "MPLS LDP Interface status failed"
1583
1584 assert (
1585 failures == 0
1586 ), "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff)
1587
1588 # Make sure that all daemons are running
1589 for i in range(1, 2):
1590 fatal_error = net["r%s" % i].checkRouterRunning()
1591 assert fatal_error == "", fatal_error
1592
1593
1594 def test_shutdown_check_stderr():
1595 global fatal_error
1596 net = get_topogen().net
1597
1598 # Skip if previous fatal error condition is raised
1599 if fatal_error != "":
1600 pytest.skip(fatal_error)
1601
1602 print("\n\n** Verifying unexpected STDERR output from daemons")
1603 print("******************************************\n")
1604
1605 if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
1606 print(
1607 "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
1608 )
1609 pytest.skip("Skipping test for Stderr output")
1610
1611 thisDir = os.path.dirname(os.path.realpath(__file__))
1612
1613 print("thisDir=" + thisDir)
1614
1615 net["r1"].stopRouter()
1616
1617 log = net["r1"].getStdErr("ripd")
1618 if log:
1619 print("\nRIPd StdErr Log:\n" + log)
1620 log = net["r1"].getStdErr("ripngd")
1621 if log:
1622 print("\nRIPngd StdErr Log:\n" + log)
1623 log = net["r1"].getStdErr("ospfd")
1624 if log:
1625 print("\nOSPFd StdErr Log:\n" + log)
1626 log = net["r1"].getStdErr("ospf6d")
1627 if log:
1628 print("\nOSPF6d StdErr Log:\n" + log)
1629 log = net["r1"].getStdErr("isisd")
1630 if log:
1631 print("\nISISd StdErr Log:\n" + log)
1632 log = net["r1"].getStdErr("bgpd")
1633 if log:
1634 print("\nBGPd StdErr Log:\n" + log)
1635
1636 log = net["r1"].getStdErr("nhrpd")
1637 if log:
1638 print("\nNHRPd StdErr Log:\n" + log)
1639
1640 log = net["r1"].getStdErr("pbrd")
1641 if log:
1642 print("\nPBRd StdErr Log:\n" + log)
1643
1644 log = net["r1"].getStdErr("babeld")
1645 if log:
1646 print("\nBABELd StdErr Log:\n" + log)
1647
1648 if net["r1"].daemon_available("ldpd"):
1649 log = net["r1"].getStdErr("ldpd")
1650 if log:
1651 print("\nLDPd StdErr Log:\n" + log)
1652 log = net["r1"].getStdErr("zebra")
1653 if log:
1654 print("\nZebra StdErr Log:\n" + log)
1655
1656
1657 def test_shutdown_check_memleak():
1658 global fatal_error
1659 net = get_topogen().net
1660
1661 # Skip if previous fatal error condition is raised
1662 if fatal_error != "":
1663 pytest.skip(fatal_error)
1664
1665 if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None:
1666 print(
1667 "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n"
1668 )
1669 pytest.skip("Skipping test for memory leaks")
1670
1671 thisDir = os.path.dirname(os.path.realpath(__file__))
1672
1673 for i in range(1, 2):
1674 net["r%s" % i].stopRouter()
1675 net["r%s" % i].report_memory_leaks(
1676 os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__)
1677 )
1678
1679
1680 if __name__ == "__main__":
1681 # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
1682 # retval = pytest.main(["-s", "--tb=no"])
1683 retval = pytest.main(["-s"])
1684 sys.exit(retval)