4 # test_all_protocol_startup.py
5 # Part of NetDEF Topology Tests
7 # Copyright (c) 2017 by
8 # Network Device Education Foundation, Inc. ("NetDEF")
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
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
26 test_all_protocol_startup.py: Test of all protocols at same time
35 from time
import sleep
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 from lib
.common_config
import (
51 required_linux_kernel_version
,
57 #####################################################
59 ## Network Topology Definition
61 #####################################################
65 router
= tgen
.add_router("r1")
66 for i
in range(0, 10):
67 tgen
.add_switch("sw%d" % i
).add_link(router
)
70 #####################################################
74 #####################################################
77 def setup_module(module
):
80 print("\n\n** %s: Setup Topology" % module
.__name
__)
81 print("******************************************\n")
83 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
84 tgen
= Topogen(build_topo
, module
.__name
__)
89 if net
["r1"].get_routertype() != "frr":
90 fatal_error
= "Test is only implemented for FRR"
91 sys
.stderr
.write("\n\nTest is only implemented for FRR - Skipping\n\n")
92 pytest
.skip(fatal_error
)
98 net
["r%s" % i
].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir
, i
))
99 net
["r%s" % i
].loadConf("ripd", "%s/r%s/ripd.conf" % (thisDir
, i
))
100 net
["r%s" % i
].loadConf("ripngd", "%s/r%s/ripngd.conf" % (thisDir
, i
))
101 net
["r%s" % i
].loadConf("ospfd", "%s/r%s/ospfd.conf" % (thisDir
, i
))
102 if net
["r1"].checkRouterVersion("<", "4.0"):
103 net
["r%s" % i
].loadConf(
104 "ospf6d", "%s/r%s/ospf6d.conf-pre-v4" % (thisDir
, i
)
107 net
["r%s" % i
].loadConf("ospf6d", "%s/r%s/ospf6d.conf" % (thisDir
, i
))
108 net
["r%s" % i
].loadConf("isisd", "%s/r%s/isisd.conf" % (thisDir
, i
))
109 net
["r%s" % i
].loadConf("bgpd", "%s/r%s/bgpd.conf" % (thisDir
, i
))
110 if net
["r%s" % i
].daemon_available("ldpd"):
111 # Only test LDPd if it's installed and Kernel >= 4.5
112 net
["r%s" % i
].loadConf("ldpd", "%s/r%s/ldpd.conf" % (thisDir
, i
))
113 net
["r%s" % i
].loadConf("sharpd")
114 net
["r%s" % i
].loadConf("nhrpd", "%s/r%s/nhrpd.conf" % (thisDir
, i
))
115 net
["r%s" % i
].loadConf("babeld", "%s/r%s/babeld.conf" % (thisDir
, i
))
116 net
["r%s" % i
].loadConf("pbrd", "%s/r%s/pbrd.conf" % (thisDir
, i
))
117 tgen
.gears
["r%s" % i
].start()
119 # For debugging after starting FRR daemons, uncomment the next line
123 def teardown_module(module
):
124 print("\n\n** %s: Shutdown Topology" % module
.__name
__)
125 print("******************************************\n")
130 def test_router_running():
135 # Skip if previous fatal error condition is raised
136 if fatal_error
!= "":
137 pytest
.skip(fatal_error
)
139 print("\n\n** Check if FRR is running on each Router node")
140 print("******************************************\n")
144 for i
in range(1, 2):
145 fatal_error
= net
["r%s" % i
].checkRouterRunning()
146 assert fatal_error
== "", fatal_error
148 # For debugging after starting FRR daemons, uncomment the next line
152 def test_error_messages_vtysh():
154 net
= get_topogen().net
156 # Skip if previous fatal error condition is raised
157 if fatal_error
!= "":
158 pytest
.skip(fatal_error
)
160 print("\n\n** Check for error messages on VTYSH")
161 print("******************************************\n")
164 for i
in range(1, 2):
166 # First checking Standard Output
169 # VTYSH output from router
170 vtystdout
= net
["r%s" % i
].cmd('vtysh -c "show version" 2> /dev/null').rstrip()
172 # Fix newlines (make them all the same)
173 vtystdout
= ("\n".join(vtystdout
.splitlines()) + "\n").rstrip()
174 # Drop everything starting with "FRRouting X.xx" message
175 vtystdout
= re
.sub(r
"FRRouting [0-9]+.*", "", vtystdout
, flags
=re
.DOTALL
)
178 print("r%s StdOut ok" % i
)
180 assert vtystdout
== "", "Vtysh StdOut Output check failed for router r%s" % i
183 # Second checking Standard Error
186 # VTYSH StdErr output from router
187 vtystderr
= net
["r%s" % i
].cmd('vtysh -c "show version" > /dev/null').rstrip()
189 # Fix newlines (make them all the same)
190 vtystderr
= ("\n".join(vtystderr
.splitlines()) + "\n").rstrip()
191 # # Drop everything starting with "FRRouting X.xx" message
192 # vtystderr = re.sub(r"FRRouting [0-9]+.*", "", vtystderr, flags=re.DOTALL)
195 print("r%s StdErr ok" % i
)
197 assert vtystderr
== "", "Vtysh StdErr Output check failed for router r%s" % i
199 # Make sure that all daemons are running
200 for i
in range(1, 2):
201 fatal_error
= net
["r%s" % i
].checkRouterRunning()
202 assert fatal_error
== "", fatal_error
205 def test_error_messages_daemons():
207 net
= get_topogen().net
209 # Skip if previous fatal error condition is raised
210 if fatal_error
!= "":
211 pytest
.skip(fatal_error
)
213 print("\n\n** Check for error messages in daemons")
214 print("******************************************\n")
218 for i
in range(1, 2):
219 log
= net
["r%s" % i
].getStdErr("ripd")
221 error_logs
+= "r%s RIPd StdErr Output:\n" % i
223 log
= net
["r%s" % i
].getStdErr("ripngd")
225 error_logs
+= "r%s RIPngd StdErr Output:\n" % i
227 log
= net
["r%s" % i
].getStdErr("ospfd")
229 error_logs
+= "r%s OSPFd StdErr Output:\n" % i
231 log
= net
["r%s" % i
].getStdErr("ospf6d")
233 error_logs
+= "r%s OSPF6d StdErr Output:\n" % i
235 log
= net
["r%s" % i
].getStdErr("isisd")
236 # ISIS shows debugging enabled status on StdErr
237 # Remove these messages
238 log
= re
.sub(r
"^IS-IS .* debugging is on.*", "", log
).rstrip()
240 error_logs
+= "r%s ISISd StdErr Output:\n" % i
242 log
= net
["r%s" % i
].getStdErr("bgpd")
244 error_logs
+= "r%s BGPd StdErr Output:\n" % i
246 if net
["r%s" % i
].daemon_available("ldpd"):
247 log
= net
["r%s" % i
].getStdErr("ldpd")
249 error_logs
+= "r%s LDPd StdErr Output:\n" % i
252 log
= net
["r1"].getStdErr("nhrpd")
253 # NHRPD shows YANG model not embedded messages
255 log
= re
.sub(r
".*YANG model.*not embedded.*", "", log
).rstrip()
257 error_logs
+= "r%s NHRPd StdErr Output:\n" % i
260 log
= net
["r1"].getStdErr("babeld")
262 error_logs
+= "r%s BABELd StdErr Output:\n" % i
265 log
= net
["r1"].getStdErr("pbrd")
267 error_logs
+= "r%s PBRd StdErr Output:\n" % i
270 log
= net
["r%s" % i
].getStdErr("zebra")
272 error_logs
+= "r%s Zebra StdErr Output:\n" % i
277 "Failed check for StdErr Output on daemons:\n%s\n" % error_logs
280 # Ignoring the issue if told to ignore (ie not yet fixed)
282 if os
.environ
.get("bamboo_TOPOTESTS_ISSUE_349") == "IGNORE":
284 "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349\n"
287 "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349"
290 assert error_logs
== "", "Daemons report errors to StdErr"
293 def test_converge_protocols():
295 net
= get_topogen().net
297 # Skip if previous fatal error condition is raised
298 if fatal_error
!= "":
299 pytest
.skip(fatal_error
)
301 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
303 print("\n\n** Waiting for protocols convergence")
304 print("******************************************\n")
306 # Not really implemented yet - just sleep 60 secs for now
309 # Make sure that all daemons are running
311 for i
in range(1, 2):
312 fatal_error
= net
["r%s" % i
].checkRouterRunning()
313 assert fatal_error
== "", fatal_error
315 print("Show that v4 routes are right\n")
316 v4_routesFile
= "%s/r%s/ipv4_routes.ref" % (thisDir
, i
)
318 net
["r%s" % i
].cmd("sort {} 2> /dev/null".format(v4_routesFile
)).rstrip()
320 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
325 "vtysh -c \"show ip route\" | sed -e '/^Codes: /,/^\\s*$/d' | sort 2> /dev/null"
329 # Drop time in last update
330 actual
= re
.sub(r
" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual
)
331 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
332 diff
= topotest
.get_textdiff(
335 title1
="Actual IP Routing Table",
336 title2
="Expected IP RoutingTable",
339 sys
.stderr
.write("r%s failed IP Routing table check:\n%s\n" % (i
, diff
))
344 assert failures
== 0, "IP Routing table failed for r%s\n%s" % (i
, diff
)
348 print("Show that v6 routes are right\n")
349 v6_routesFile
= "%s/r%s/ipv6_routes.ref" % (thisDir
, i
)
351 net
["r%s" % i
].cmd("sort {} 2> /dev/null".format(v6_routesFile
)).rstrip()
353 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
358 "vtysh -c \"show ipv6 route\" | sed -e '/^Codes: /,/^\\s*$/d' | sort 2> /dev/null"
362 # Drop time in last update
363 actual
= re
.sub(r
" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual
)
364 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
365 diff
= topotest
.get_textdiff(
368 title1
="Actual IPv6 Routing Table",
369 title2
="Expected IPv6 RoutingTable",
372 sys
.stderr
.write("r%s failed IPv6 Routing table check:\n%s\n" % (i
, diff
))
377 assert failures
== 0, "IPv6 Routing table failed for r%s\n%s" % (i
, diff
)
380 def route_get_nhg_id(route_str
):
381 net
= get_topogen().net
382 output
= net
["r1"].cmd('vtysh -c "show ip route %s nexthop-group"' % route_str
)
383 match
= re
.search(r
"Nexthop Group ID: (\d+)", output
)
384 assert match
is not None, (
385 "Nexthop Group ID not found for sharpd route %s" % route_str
388 nhg_id
= int(match
.group(1))
392 def verify_nexthop_group(nhg_id
, recursive
=False, ecmp
=0):
393 net
= get_topogen().net
402 while not found
and count
< 10:
404 # Verify NHG is valid/installed
405 output
= net
["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id
)
406 valid
= re
.search(r
"Valid", output
)
412 if ecmp
or recursive
:
413 ecmpcount
= re
.search(r
"Depends:.*\n", output
)
414 if ecmpcount
is None:
419 # list of IDs in group
420 depends
= re
.findall(r
"\((\d+)\)", ecmpcount
.group(0))
423 if len(depends
) != ecmp
:
428 # If recursive, we need to look at its resolved group
429 if len(depends
) != 1:
434 resolved_id
= int(depends
[0])
435 verify_nexthop_group(resolved_id
, False)
437 installed
= re
.search(r
"Installed", output
)
438 if installed
is None:
444 assert valid
is not None, "Nexthop Group ID=%d not marked Valid" % nhg_id
445 if ecmp
or recursive
:
446 assert ecmpcount
is not None, "Nexthop Group ID=%d has no depends" % nhg_id
448 assert len(depends
) == ecmp
, (
449 "Nexthop Group ID=%d doesn't match ecmp size" % nhg_id
452 assert len(depends
) == 1, (
453 "Nexthop Group ID=%d should only have one recursive depend" % nhg_id
456 assert installed
is not None, (
457 "Nexthop Group ID=%d not marked Installed" % nhg_id
461 def verify_route_nexthop_group(route_str
, recursive
=False, ecmp
=0):
462 # Verify route and that zebra created NHGs for and they are valid/installed
463 nhg_id
= route_get_nhg_id(route_str
)
464 verify_nexthop_group(nhg_id
, recursive
, ecmp
)
467 def test_nexthop_groups():
469 net
= get_topogen().net
471 # Skip if previous fatal error condition is raised
472 if fatal_error
!= "":
473 pytest
.skip(fatal_error
)
475 print("\n\n** Verifying Nexthop Groups")
476 print("******************************************\n")
478 ### Nexthop Group Tests
482 # Create a lib nexthop-group
484 'vtysh -c "c t" -c "nexthop-group basic" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"'
487 # Create with sharpd using nexthop-group
488 net
["r1"].cmd('vtysh -c "sharp install routes 2.2.2.1 nexthop-group basic 1"')
489 verify_route_nexthop_group("2.2.2.1/32")
494 'vtysh -c "c t" -c "nexthop-group connected" -c "nexthop r1-eth1" -c "nexthop r1-eth2"'
497 net
["r1"].cmd('vtysh -c "sharp install routes 2.2.2.2 nexthop-group connected 1"')
498 verify_route_nexthop_group("2.2.2.2/32")
503 'vtysh -c "c t" -c "nexthop-group basic-recursive" -c "nexthop 2.2.2.1"'
507 'vtysh -c "sharp install routes 3.3.3.1 nexthop-group basic-recursive 1"'
510 verify_route_nexthop_group("3.3.3.1/32", True)
515 'vtysh -c "c t" -c "nexthop-group duplicate" -c "nexthop 2.2.2.1" -c "nexthop 1.1.1.1"'
518 net
["r1"].cmd('vtysh -c "sharp install routes 3.3.3.2 nexthop-group duplicate 1"')
520 verify_route_nexthop_group("3.3.3.2/32")
525 'vtysh -c "c t" -c "nexthop-group fourA" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2" \
526 -c "nexthop 1.1.1.3" -c "nexthop 1.1.1.4"'
529 net
["r1"].cmd('vtysh -c "sharp install routes 4.4.4.1 nexthop-group fourA 1"')
531 verify_route_nexthop_group("4.4.4.1/32")
534 'vtysh -c "c t" -c "nexthop-group fourB" -c "nexthop 1.1.1.5" -c "nexthop 1.1.1.6" \
535 -c "nexthop 1.1.1.7" -c "nexthop 1.1.1.8"'
538 net
["r1"].cmd('vtysh -c "sharp install routes 4.4.4.2 nexthop-group fourB 1"')
540 verify_route_nexthop_group("4.4.4.2/32")
542 ## Recursive to 8-Way ECMP
545 'vtysh -c "c t" -c "nexthop-group eight-recursive" -c "nexthop 4.4.4.1" -c "nexthop 4.4.4.2"'
549 'vtysh -c "sharp install routes 5.5.5.1 nexthop-group eight-recursive 1"'
552 verify_route_nexthop_group("5.5.5.1/32")
554 ## 4-way ECMP Routes Pointing to Each Other
556 # This is to check for a bug with NH resolution where
557 # routes would infintely resolve to each other blowing
558 # up the resolved-> nexthop pointer.
561 'vtysh -c "c t" -c "nexthop-group infinite-recursive" -c "nexthop 6.6.6.1" -c "nexthop 6.6.6.2" \
562 -c "nexthop 6.6.6.3" -c "nexthop 6.6.6.4"'
565 # static route nexthops can recurse to
567 net
["r1"].cmd('vtysh -c "c t" -c "ip route 6.6.6.0/24 1.1.1.1"')
569 # Make routes that point to themselves in ecmp
572 'vtysh -c "sharp install routes 6.6.6.4 nexthop-group infinite-recursive 1"'
577 'vtysh -c "sharp install routes 6.6.6.3 nexthop-group infinite-recursive 1"'
582 'vtysh -c "sharp install routes 6.6.6.2 nexthop-group infinite-recursive 1"'
587 'vtysh -c "sharp install routes 6.6.6.1 nexthop-group infinite-recursive 1"'
590 # Get routes and test if has too many (duplicate) nexthops
593 nhg_id
= route_get_nhg_id("6.6.6.1/32")
594 while (len(dups
) != 3) and count
< 10:
595 output
= net
["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id
)
597 dups
= re
.findall(r
"(via 1\.1\.1\.1)", output
)
602 # Should find 3, itself is inactive
603 assert len(dups
) == 3, (
604 "Route 6.6.6.1/32 with Nexthop Group ID=%d has wrong number of resolved nexthops"
608 ## Remove all NHG routes
610 net
["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.1 1"')
611 net
["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.2 1"')
612 net
["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.1 1"')
613 net
["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.2 1"')
614 net
["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.1 1"')
615 net
["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.2 1"')
616 net
["r1"].cmd('vtysh -c "sharp remove routes 5.5.5.1 1"')
617 net
["r1"].cmd('vtysh -c "sharp remove routes 6.6.6.1 4"')
618 net
["r1"].cmd('vtysh -c "c t" -c "no ip route 6.6.6.0/24 1.1.1.1"')
621 def test_rip_status():
623 net
= get_topogen().net
625 # Skip if previous fatal error condition is raised
626 if fatal_error
!= "":
627 pytest
.skip(fatal_error
)
629 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
631 print("\n\n** Verifying RIP status")
632 print("******************************************\n")
634 for i
in range(1, 2):
635 refTableFile
= "%s/r%s/rip_status.ref" % (thisDir
, i
)
636 if os
.path
.isfile(refTableFile
):
637 # Read expected result from file
638 expected
= open(refTableFile
).read().rstrip()
639 # Fix newlines (make them all the same)
640 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
642 # Actual output from router
645 .cmd('vtysh -c "show ip rip status" 2> /dev/null')
648 # Drop time in next due
649 actual
= re
.sub(r
"in [0-9]+ seconds", "in XX seconds", actual
)
650 # Drop time in last update
651 actual
= re
.sub(r
" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual
)
652 # Fix newlines (make them all the same)
653 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
656 diff
= topotest
.get_textdiff(
659 title1
="actual IP RIP status",
660 title2
="expected IP RIP status",
663 # Empty string if it matches, otherwise diff contains unified diff
665 sys
.stderr
.write("r%s failed IP RIP status check:\n%s\n" % (i
, diff
))
670 assert failures
== 0, "IP RIP status failed for router r%s:\n%s" % (i
, diff
)
672 # Make sure that all daemons are running
673 for i
in range(1, 2):
674 fatal_error
= net
["r%s" % i
].checkRouterRunning()
675 assert fatal_error
== "", fatal_error
678 def test_ripng_status():
680 net
= get_topogen().net
682 # Skip if previous fatal error condition is raised
683 if fatal_error
!= "":
684 pytest
.skip(fatal_error
)
686 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
688 print("\n\n** Verifying RIPng status")
689 print("******************************************\n")
691 for i
in range(1, 2):
692 refTableFile
= "%s/r%s/ripng_status.ref" % (thisDir
, i
)
693 if os
.path
.isfile(refTableFile
):
694 # Read expected result from file
695 expected
= open(refTableFile
).read().rstrip()
696 # Fix newlines (make them all the same)
697 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
699 # Actual output from router
702 .cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null')
705 # Mask out Link-Local mac address portion. They are random...
706 actual
= re
.sub(r
" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual
)
707 # Drop time in next due
708 actual
= re
.sub(r
"in [0-9]+ seconds", "in XX seconds", actual
)
709 # Drop time in last update
710 actual
= re
.sub(r
" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual
)
711 # Fix newlines (make them all the same)
712 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
715 diff
= topotest
.get_textdiff(
718 title1
="actual IPv6 RIPng status",
719 title2
="expected IPv6 RIPng status",
722 # Empty string if it matches, otherwise diff contains unified diff
725 "r%s failed IPv6 RIPng status check:\n%s\n" % (i
, diff
)
731 assert failures
== 0, "IPv6 RIPng status failed for router r%s:\n%s" % (
736 # Make sure that all daemons are running
737 for i
in range(1, 2):
738 fatal_error
= net
["r%s" % i
].checkRouterRunning()
739 assert fatal_error
== "", fatal_error
742 def test_ospfv2_interfaces():
744 net
= get_topogen().net
746 # Skip if previous fatal error condition is raised
747 if fatal_error
!= "":
748 pytest
.skip(fatal_error
)
750 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
752 print("\n\n** Verifying OSPFv2 interfaces")
753 print("******************************************\n")
755 for i
in range(1, 2):
756 refTableFile
= "%s/r%s/show_ip_ospf_interface.ref" % (thisDir
, i
)
757 if os
.path
.isfile(refTableFile
):
758 # Read expected result from file
759 expected
= open(refTableFile
).read().rstrip()
760 # Fix newlines (make them all the same)
761 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
763 # Actual output from router
766 .cmd('vtysh -c "show ip ospf interface" 2> /dev/null')
769 # Mask out Bandwidth portion. They may change..
770 actual
= re
.sub(r
"BW [0-9]+ Mbit", "BW XX Mbit", actual
)
771 actual
= re
.sub(r
"ifindex [0-9]+", "ifindex X", actual
)
773 # Drop time in next due
774 actual
= re
.sub(r
"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual
)
776 r
"Hello due in [0-9\.]+ usecs", "Hello due in XX.XXXs", actual
778 # Fix 'MTU mismatch detection: enabled' vs 'MTU mismatch detection:enabled' - accept both
780 r
"MTU mismatch detection:([a-z]+.*)",
781 r
"MTU mismatch detection: \1",
784 # Fix newlines (make them all the same)
785 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
788 diff
= topotest
.get_textdiff(
791 title1
="actual SHOW IP OSPF INTERFACE",
792 title2
="expected SHOW IP OSPF INTERFACE",
795 # Empty string if it matches, otherwise diff contains unified diff
798 "r%s failed SHOW IP OSPF INTERFACE check:\n%s\n" % (i
, diff
)
804 # Ignoring the issue if told to ignore (ie not yet fixed)
806 if os
.environ
.get("bamboo_TOPOTESTS_ISSUE_348") == "IGNORE":
808 "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348\n"
811 "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348"
816 ), "SHOW IP OSPF INTERFACE failed for router r%s:\n%s" % (i
, diff
)
818 # Make sure that all daemons are running
819 for i
in range(1, 2):
820 fatal_error
= net
["r%s" % i
].checkRouterRunning()
821 assert fatal_error
== "", fatal_error
824 def test_isis_interfaces():
826 net
= get_topogen().net
828 # Skip if previous fatal error condition is raised
829 if fatal_error
!= "":
830 pytest
.skip(fatal_error
)
832 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
834 print("\n\n** Verifying ISIS interfaces")
835 print("******************************************\n")
837 for i
in range(1, 2):
838 refTableFile
= "%s/r%s/show_isis_interface_detail.ref" % (thisDir
, i
)
839 if os
.path
.isfile(refTableFile
):
840 # Read expected result from file
841 expected
= open(refTableFile
).read().rstrip()
842 # Fix newlines (make them all the same)
843 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
845 # Actual output from router
848 .cmd('vtysh -c "show isis interface detail" 2> /dev/null')
851 # Mask out Link-Local mac address portion. They are random...
852 actual
= re
.sub(r
"fe80::[0-9a-f:]+", "fe80::XXXX:XXXX:XXXX:XXXX", actual
)
853 # Mask out SNPA mac address portion. They are random...
854 actual
= re
.sub(r
"SNPA: [0-9a-f\.]+", "SNPA: XXXX.XXXX.XXXX", actual
)
855 # Mask out Circuit ID number
856 actual
= re
.sub(r
"Circuit Id: 0x[0-9a-f]+", "Circuit Id: 0xXX", actual
)
857 # Fix newlines (make them all the same)
858 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
861 diff
= topotest
.get_textdiff(
864 title1
="actual SHOW ISIS INTERFACE DETAIL",
865 title2
="expected SHOW ISIS OSPF6 INTERFACE DETAIL",
868 # Empty string if it matches, otherwise diff contains unified diff
871 "r%s failed SHOW ISIS INTERFACE DETAIL check:\n%s\n" % (i
, diff
)
879 ), "SHOW ISIS INTERFACE DETAIL failed for router r%s:\n%s" % (i
, diff
)
881 # Make sure that all daemons are running
882 for i
in range(1, 2):
883 fatal_error
= net
["r%s" % i
].checkRouterRunning()
884 assert fatal_error
== "", fatal_error
887 def test_bgp_summary():
889 net
= get_topogen().net
891 # Skip if previous fatal error condition is raised
892 if fatal_error
!= "":
893 pytest
.skip(fatal_error
)
895 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
897 print("\n\n** Verifying BGP Summary")
898 print("******************************************\n")
900 for i
in range(1, 2):
901 refTableFile
= "%s/r%s/show_ip_bgp_summary.ref" % (thisDir
, i
)
902 if os
.path
.isfile(refTableFile
):
903 # Read expected result from file
904 expected_original
= open(refTableFile
).read().rstrip()
908 "remote-as internal",
909 "remote-as external",
912 "neighbor 192.168.7.10",
913 "neighbor 192.168.7.10",
914 "neighbor fc00:0:0:8::1000",
917 "remote-as internal terse",
918 "remote-as external terse",
919 "remote-as 100 terse",
920 "remote-as 123 terse",
921 "neighbor 192.168.7.10 terse",
922 "neighbor 192.168.7.10 terse",
923 "neighbor fc00:0:0:8::1000 terse",
924 "neighbor 10.0.0.1 terse",
926 # Actual output from router
930 'vtysh -c "show ip bgp summary ' + arguments
+ '" 2> /dev/null'
935 # Mask out "using XXiXX bytes" portion. They are random...
936 actual
= re
.sub(r
"using [0-9]+ bytes", "using XXXX bytes", actual
)
937 # Mask out "using XiXXX KiB" portion. They are random...
938 actual
= re
.sub(r
"using [0-9]+ KiB", "using XXXX KiB", actual
)
940 # Remove extra summaries which exist with newer versions
942 # Remove summary lines (changed recently)
943 actual
= re
.sub(r
"Total number.*", "", actual
)
944 actual
= re
.sub(r
"Displayed.*", "", actual
)
945 # Remove IPv4 Unicast Summary (Title only)
946 actual
= re
.sub(r
"IPv4 Unicast Summary \(VRF default\):", "", actual
)
947 # Remove IPv4 Multicast Summary (all of it)
948 actual
= re
.sub(r
"IPv4 Multicast Summary \(VRF default\):", "", actual
)
949 actual
= re
.sub(r
"No IPv4 Multicast neighbor is configured", "", actual
)
950 # Remove IPv4 VPN Summary (all of it)
951 actual
= re
.sub(r
"IPv4 VPN Summary \(VRF default\):", "", actual
)
952 actual
= re
.sub(r
"No IPv4 VPN neighbor is configured", "", actual
)
953 # Remove IPv4 Encap Summary (all of it)
954 actual
= re
.sub(r
"IPv4 Encap Summary \(VRF default\):", "", actual
)
955 actual
= re
.sub(r
"No IPv4 Encap neighbor is configured", "", actual
)
956 # Remove Unknown Summary (all of it)
957 actual
= re
.sub(r
"Unknown Summary \(VRF default\):", "", actual
)
958 actual
= re
.sub(r
"No Unknown neighbor is configured", "", actual
)
959 # Make Connect/Active/Idle the same (change them all to Active)
960 actual
= re
.sub(r
" Connect ", " Active ", actual
)
961 actual
= re
.sub(r
" Idle ", " Active ", actual
)
964 r
"IPv4 labeled-unicast Summary \(VRF default\):", "", actual
967 r
"No IPv4 labeled-unicast neighbor is configured", "", actual
970 expected
= expected_original
971 # apply argumentss on expected output
972 if "internal" in arguments
or "remote-as 100" in arguments
:
973 expected
= re
.sub(r
".+\s+200\s+.+", "", expected
)
974 elif "external" in arguments
:
975 expected
= re
.sub(r
".+\s+100\s+.+Active.+", "", expected
)
976 elif "remote-as 123" in arguments
:
978 r
"(192.168.7.(1|2)0|fc00:0:0:8::(1|2)000).+Active.+",
982 expected
= re
.sub(r
"\nNeighbor.+Desc", "", expected
)
983 expected
= expected
+ "% No matching neighbor\n"
984 elif "192.168.7.10" in arguments
:
986 r
"(192.168.7.20|fc00:0:0:8::(1|2)000).+Active.+", "", expected
988 elif "fc00:0:0:8::1000" in arguments
:
990 r
"(192.168.7.(1|2)0|fc00:0:0:8::2000).+Active.+", "", expected
992 elif "10.0.0.1" in arguments
:
993 expected
= "No such neighbor in this view/vrf"
995 if "terse" in arguments
:
996 expected
= re
.sub(r
"BGP table version .+", "", expected
)
997 expected
= re
.sub(r
"RIB entries .+", "", expected
)
998 expected
= re
.sub(r
"Peers [0-9]+, using .+", "", expected
)
1001 actual
= actual
.lstrip().rstrip()
1002 expected
= expected
.lstrip().rstrip()
1003 actual
= re
.sub(r
"\n+", "\n", actual
)
1004 expected
= re
.sub(r
"\n+", "\n", expected
)
1006 # reapply initial formatting
1007 if "terse" in arguments
:
1008 actual
= re
.sub(r
" vrf-id 0\n", " vrf-id 0\n\n", actual
)
1009 expected
= re
.sub(r
" vrf-id 0\n", " vrf-id 0\n\n", expected
)
1011 actual
= re
.sub(r
"KiB of memory\n", "KiB of memory\n\n", actual
)
1012 expected
= re
.sub(r
"KiB of memory\n", "KiB of memory\n\n", expected
)
1014 # realign expected neighbor columns if needed
1017 re
.search(r
"(Neighbor\s+V\s+)", actual
).group(1).find("V")
1020 re
.search(r
"(Neighbor\s+V\s+)", expected
).group(1).find("V")
1022 idx_diff
= idx_expected
- idx_actual
1024 # Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
1025 expected
= re
.sub(" " * idx_diff
+ "V ", "V ", expected
)
1026 # 192.168.7.10 4 100 0 0 0 0 0 never Active
1027 expected
= re
.sub(" " * idx_diff
+ "4 ", "4 ", expected
)
1028 except AttributeError:
1031 # Fix newlines (make them all the same)
1032 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
1033 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
1036 diff
= topotest
.get_textdiff(
1039 title1
="actual SHOW IP BGP SUMMARY " + arguments
.upper(),
1040 title2
="expected SHOW IP BGP SUMMARY " + arguments
.upper(),
1043 # Empty string if it matches, otherwise diff contains unified diff
1046 "r%s failed SHOW IP BGP SUMMARY check:\n%s\n" % (i
, diff
)
1054 ), "SHOW IP BGP SUMMARY failed for router r%s:\n%s" % (
1059 # Make sure that all daemons are running
1060 for i
in range(1, 2):
1061 fatal_error
= net
["r%s" % i
].checkRouterRunning()
1062 assert fatal_error
== "", fatal_error
1065 def test_bgp_ipv6_summary():
1067 net
= get_topogen().net
1069 # Skip if previous fatal error condition is raised
1070 if fatal_error
!= "":
1071 pytest
.skip(fatal_error
)
1073 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
1075 print("\n\n** Verifying BGP IPv6 Summary")
1076 print("******************************************\n")
1078 for i
in range(1, 2):
1079 refTableFile
= "%s/r%s/show_bgp_ipv6_summary.ref" % (thisDir
, i
)
1080 if os
.path
.isfile(refTableFile
):
1081 # Read expected result from file
1082 expected
= open(refTableFile
).read().rstrip()
1083 # Fix newlines (make them all the same)
1084 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
1086 # Actual output from router
1089 .cmd('vtysh -c "show bgp ipv6 summary" 2> /dev/null')
1092 # Mask out "using XXiXX bytes" portion. They are random...
1093 actual
= re
.sub(r
"using [0-9]+ bytes", "using XXXX bytes", actual
)
1094 # Mask out "using XiXXX KiB" portion. They are random...
1095 actual
= re
.sub(r
"using [0-9]+ KiB", "using XXXX KiB", actual
)
1097 # Remove extra summaries which exist with newer versions
1099 # Remove summary lines (changed recently)
1100 actual
= re
.sub(r
"Total number.*", "", actual
)
1101 actual
= re
.sub(r
"Displayed.*", "", actual
)
1102 # Remove IPv4 Unicast Summary (Title only)
1103 actual
= re
.sub(r
"IPv6 Unicast Summary \(VRF default\):", "", actual
)
1104 # Remove IPv4 Multicast Summary (all of it)
1105 actual
= re
.sub(r
"IPv6 Multicast Summary \(VRF default\):", "", actual
)
1106 actual
= re
.sub(r
"No IPv6 Multicast neighbor is configured", "", actual
)
1107 # Remove IPv4 VPN Summary (all of it)
1108 actual
= re
.sub(r
"IPv6 VPN Summary \(VRF default\):", "", actual
)
1109 actual
= re
.sub(r
"No IPv6 VPN neighbor is configured", "", actual
)
1110 # Remove IPv4 Encap Summary (all of it)
1111 actual
= re
.sub(r
"IPv6 Encap Summary \(VRF default\):", "", actual
)
1112 actual
= re
.sub(r
"No IPv6 Encap neighbor is configured", "", actual
)
1113 # Remove Unknown Summary (all of it)
1114 actual
= re
.sub(r
"Unknown Summary \(VRF default\):", "", actual
)
1115 actual
= re
.sub(r
"No Unknown neighbor is configured", "", actual
)
1116 # Make Connect/Active/Idle the same (change them all to Active)
1117 actual
= re
.sub(r
" Connect ", " Active ", actual
)
1118 actual
= re
.sub(r
" Idle ", " Active ", actual
)
1120 # Remove Labeled Unicast Summary (all of it)
1122 r
"IPv6 labeled-unicast Summary \(VRF default\):", "", actual
1125 r
"No IPv6 labeled-unicast neighbor is configured", "", actual
1129 actual
= actual
.lstrip()
1130 actual
= actual
.rstrip()
1132 # Fix newlines (make them all the same)
1133 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
1136 diff
= topotest
.get_textdiff(
1139 title1
="actual SHOW BGP IPv6 SUMMARY",
1140 title2
="expected SHOW BGP IPv6 SUMMARY",
1143 # Empty string if it matches, otherwise diff contains unified diff
1146 "r%s failed SHOW BGP IPv6 SUMMARY check:\n%s\n" % (i
, diff
)
1152 assert failures
== 0, "SHOW BGP IPv6 SUMMARY failed for router r%s:\n%s" % (
1157 # Make sure that all daemons are running
1158 for i
in range(1, 2):
1159 fatal_error
= net
["r%s" % i
].checkRouterRunning()
1160 assert fatal_error
== "", fatal_error
1165 net
= get_topogen().net
1167 # Skip if previous fatal error condition is raised
1168 if fatal_error
!= "":
1169 pytest
.skip(fatal_error
)
1171 print("\n\n**** Test that nexthop tracking is at least nominally working ****\n")
1173 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
1175 for i
in range(1, 2):
1176 nhtFile
= "%s/r%s/ip_nht.ref" % (thisDir
, i
)
1177 expected
= open(nhtFile
).read().rstrip()
1178 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
1180 actual
= net
["r%s" % i
].cmd('vtysh -c "show ip nht" 2> /dev/null').rstrip()
1181 actual
= re
.sub(r
"fd [0-9]+", "fd XX", actual
)
1182 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
1184 diff
= topotest
.get_textdiff(
1187 title1
="Actual `show ip nht`",
1188 title2
="Expected `show ip nht`",
1192 assert 0, "r%s failed ip nht check:\n%s\n" % (i
, diff
)
1194 print("show ip nht is ok\n")
1196 nhtFile
= "%s/r%s/ipv6_nht.ref" % (thisDir
, i
)
1197 expected
= open(nhtFile
).read().rstrip()
1198 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
1200 actual
= net
["r%s" % i
].cmd('vtysh -c "show ipv6 nht" 2> /dev/null').rstrip()
1201 actual
= re
.sub(r
"fd [0-9]+", "fd XX", actual
)
1202 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
1204 diff
= topotest
.get_textdiff(
1207 title1
="Actual `show ip nht`",
1208 title2
="Expected `show ip nht`",
1212 assert 0, "r%s failed ipv6 nht check:\n%s\n" % (i
, diff
)
1214 print("show ipv6 nht is ok\n")
1217 def test_bgp_ipv4():
1219 net
= get_topogen().net
1221 # Skip if previous fatal error condition is raised
1222 if fatal_error
!= "":
1223 pytest
.skip(fatal_error
)
1225 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
1227 print("\n\n** Verifying BGP IPv4")
1228 print("******************************************\n")
1230 for i
in range(1, 2):
1232 for refTableFile
in glob
.glob("%s/r%s/show_bgp_ipv4*.ref" % (thisDir
, i
)):
1233 if os
.path
.isfile(refTableFile
):
1234 # Read expected result from file
1235 expected
= open(refTableFile
).read().rstrip()
1236 # Fix newlines (make them all the same)
1237 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
1239 # Actual output from router
1241 net
["r%s" % i
].cmd('vtysh -c "show bgp ipv4" 2> /dev/null').rstrip()
1243 # Remove summary line (changed recently)
1244 actual
= re
.sub(r
"Total number.*", "", actual
)
1245 actual
= re
.sub(r
"Displayed.*", "", actual
)
1246 actual
= actual
.rstrip()
1247 # Fix newlines (make them all the same)
1248 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
1251 diff
= topotest
.get_textdiff(
1254 title1
="actual SHOW BGP IPv4",
1255 title2
="expected SHOW BGP IPv4",
1258 # Empty string if it matches, otherwise diff contains unified diff
1260 diffresult
[refTableFile
] = diff
1263 print("template %s matched: r%s ok" % (refTableFile
, i
))
1267 resultstr
= "No template matched.\n"
1268 for f
in diffresult
.keys():
1269 resultstr
+= "template %s: r%s failed SHOW BGP IPv4 check:\n%s\n" % (
1274 raise AssertionError(
1275 "SHOW BGP IPv4 failed for router r%s:\n%s" % (i
, resultstr
)
1278 # Make sure that all daemons are running
1279 for i
in range(1, 2):
1280 fatal_error
= net
["r%s" % i
].checkRouterRunning()
1281 assert fatal_error
== "", fatal_error
1284 def test_bgp_ipv6():
1286 net
= get_topogen().net
1288 # Skip if previous fatal error condition is raised
1289 if fatal_error
!= "":
1290 pytest
.skip(fatal_error
)
1292 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
1294 print("\n\n** Verifying BGP IPv6")
1295 print("******************************************\n")
1297 for i
in range(1, 2):
1299 for refTableFile
in glob
.glob("%s/r%s/show_bgp_ipv6*.ref" % (thisDir
, i
)):
1300 if os
.path
.isfile(refTableFile
):
1301 # Read expected result from file
1302 expected
= open(refTableFile
).read().rstrip()
1303 # Fix newlines (make them all the same)
1304 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
1306 # Actual output from router
1308 net
["r%s" % i
].cmd('vtysh -c "show bgp ipv6" 2> /dev/null').rstrip()
1310 # Remove summary line (changed recently)
1311 actual
= re
.sub(r
"Total number.*", "", actual
)
1312 actual
= re
.sub(r
"Displayed.*", "", actual
)
1313 actual
= actual
.rstrip()
1314 # Fix newlines (make them all the same)
1315 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
1318 diff
= topotest
.get_textdiff(
1321 title1
="actual SHOW BGP IPv6",
1322 title2
="expected SHOW BGP IPv6",
1325 # Empty string if it matches, otherwise diff contains unified diff
1327 diffresult
[refTableFile
] = diff
1330 print("template %s matched: r%s ok" % (refTableFile
, i
))
1333 resultstr
= "No template matched.\n"
1334 for f
in diffresult
.keys():
1335 resultstr
+= "template %s: r%s failed SHOW BGP IPv6 check:\n%s\n" % (
1340 raise AssertionError(
1341 "SHOW BGP IPv6 failed for router r%s:\n%s" % (i
, resultstr
)
1344 # Make sure that all daemons are running
1345 for i
in range(1, 2):
1346 fatal_error
= net
["r%s" % i
].checkRouterRunning()
1347 assert fatal_error
== "", fatal_error
1350 def test_route_map():
1352 net
= get_topogen().net
1354 if fatal_error
!= "":
1355 pytest
.skip(fatal_error
)
1357 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
1359 print("\n\n** Verifying some basic routemap forward references\n")
1360 print("*******************************************************\n")
1362 for i
in range(1, 2):
1363 refroutemap
= "%s/r%s/show_route_map.ref" % (thisDir
, i
)
1364 if os
.path
.isfile(refroutemap
):
1365 expected
= open(refroutemap
).read().rstrip()
1366 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
1369 net
["r%s" % i
].cmd('vtysh -c "show route-map" 2> /dev/null').rstrip()
1371 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
1373 diff
= topotest
.get_textdiff(
1376 title1
="actual show route-map",
1377 title2
="expected show route-map",
1382 "r%s failed show route-map command Check:\n%s\n" % (i
, diff
)
1390 ), "Show route-map command failed for router r%s:\n%s" % (i
, diff
)
1393 def test_nexthop_groups_with_route_maps():
1395 net
= get_topogen().net
1397 # Skip if previous fatal error condition is raised
1398 if fatal_error
!= "":
1399 pytest
.skip(fatal_error
)
1401 print("\n\n** Verifying Nexthop Groups With Route-Maps")
1402 print("******************************************\n")
1404 ### Nexthop Group With Route-Map Tests
1406 # Create a lib nexthop-group
1408 'vtysh -c "c t" -c "nexthop-group test" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"'
1411 ## Route-Map Proto Source
1413 route_str
= "2.2.2.1"
1414 src_str
= "192.168.0.1"
1417 'vtysh -c "c t" -c "route-map NH-SRC permit 111" -c "set src %s"' % src_str
1419 net
["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NH-SRC"')
1421 net
["r1"].cmd('vtysh -c "sharp install routes %s nexthop-group test 1"' % route_str
)
1423 verify_route_nexthop_group("%s/32" % route_str
)
1425 # Only a valid test on linux using nexthop objects
1426 if sys
.platform
.startswith("linux"):
1427 output
= net
["r1"].cmd("ip route show %s/32" % route_str
)
1428 match
= re
.search(r
"src %s" % src_str
, output
)
1429 assert match
is not None, "Route %s/32 not installed with src %s" % (
1434 # Remove NHG routes and route-map
1435 net
["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % route_str
)
1436 net
["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NH-SRC"')
1438 'vtysh -c "c t" -c "no route-map NH-SRC permit 111" # -c "set src %s"' % src_str
1440 net
["r1"].cmd('vtysh -c "c t" -c "no route-map NH-SRC"')
1442 ## Route-Map Deny/Permit with same nexthop group
1444 permit_route_str
= "3.3.3.1"
1445 deny_route_str
= "3.3.3.2"
1448 'vtysh -c "c t" -c "ip prefix-list NOPE seq 5 permit %s/32"' % permit_route_str
1451 'vtysh -c "c t" -c "route-map NOPE permit 111" -c "match ip address prefix-list NOPE"'
1453 net
["r1"].cmd('vtysh -c "c t" -c "route-map NOPE deny 222"')
1454 net
["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NOPE"')
1456 # This route should be permitted
1458 'vtysh -c "sharp install routes %s nexthop-group test 1"' % permit_route_str
1461 verify_route_nexthop_group("%s/32" % permit_route_str
)
1463 # This route should be denied
1465 'vtysh -c "sharp install routes %s nexthop-group test 1"' % deny_route_str
1468 nhg_id
= route_get_nhg_id(deny_route_str
)
1469 output
= net
["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id
)
1471 match
= re
.search(r
"Valid", output
)
1472 assert match
is None, "Nexthop Group ID=%d should not be marked Valid" % nhg_id
1474 match
= re
.search(r
"Installed", output
)
1475 assert match
is None, "Nexthop Group ID=%d should not be marked Installed" % nhg_id
1477 # Remove NHG routes and route-map
1478 net
["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % permit_route_str
)
1479 net
["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % deny_route_str
)
1480 net
["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NOPE"')
1481 net
["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE permit 111"')
1482 net
["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE deny 222"')
1483 net
["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE"')
1485 'vtysh -c "c t" -c "no ip prefix-list NOPE seq 5 permit %s/32"'
1490 def test_nexthop_group_replace():
1492 net
= get_topogen().net
1494 # Skip if previous fatal error condition is raised
1495 if fatal_error
!= "":
1496 pytest
.skip(fatal_error
)
1498 print("\n\n** Verifying Nexthop Groups")
1499 print("******************************************\n")
1501 ### Nexthop Group Tests
1503 ## 2-Way ECMP Directly Connected
1506 '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"'
1509 # At the moment there is absolutely no real easy way to query sharpd
1510 # for the nexthop group actually installed. If it is not installed
1511 # sharpd will just transmit the nexthops down instead of the nexthop
1512 # group id. Leading to a situation where the replace is not actually
1513 # being tested. So let's just wait some time here because this
1514 # is hard and this test fails all the time
1517 # Create with sharpd using nexthop-group
1518 net
["r1"].cmd('vtysh -c "sharp install routes 3.3.3.1 nexthop-group replace 1"')
1520 verify_route_nexthop_group("3.3.3.1/32")
1522 # Change the nexthop group
1524 '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"'
1527 # Verify it updated. We can just check install and ecmp count here.
1528 verify_route_nexthop_group("3.3.3.1/32", False, 3)
1531 def test_mpls_interfaces():
1533 net
= get_topogen().net
1535 # Skip if previous fatal error condition is raised
1536 if fatal_error
!= "":
1537 pytest
.skip(fatal_error
)
1539 # Skip if no LDP installed or old kernel
1540 if net
["r1"].daemon_available("ldpd") == False:
1541 pytest
.skip("No MPLS or kernel < 4.5")
1543 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
1545 print("\n\n** Verifying MPLS Interfaces")
1546 print("******************************************\n")
1548 for i
in range(1, 2):
1549 refTableFile
= "%s/r%s/show_mpls_ldp_interface.ref" % (thisDir
, i
)
1550 if os
.path
.isfile(refTableFile
):
1551 # Read expected result from file
1552 expected
= open(refTableFile
).read().rstrip()
1553 # Fix newlines (make them all the same)
1554 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
1556 # Actual output from router
1559 .cmd('vtysh -c "show mpls ldp interface" 2> /dev/null')
1562 # Mask out Timer in Uptime
1563 actual
= re
.sub(r
" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual
)
1564 # Fix newlines (make them all the same)
1565 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
1568 diff
= topotest
.get_textdiff(
1571 title1
="actual MPLS LDP interface status",
1572 title2
="expected MPLS LDP interface status",
1575 # Empty string if it matches, otherwise diff contains unified diff
1578 "r%s failed MPLS LDP Interface status Check:\n%s\n" % (i
, diff
)
1585 fatal_error
= "MPLS LDP Interface status failed"
1589 ), "MPLS LDP Interface status failed for router r%s:\n%s" % (i
, diff
)
1591 # Make sure that all daemons are running
1592 for i
in range(1, 2):
1593 fatal_error
= net
["r%s" % i
].checkRouterRunning()
1594 assert fatal_error
== "", fatal_error
1597 def test_resilient_nexthop_group():
1598 net
= get_topogen().net
1600 result
= required_linux_kernel_version("5.19")
1601 if result
is not True:
1602 pytest
.skip("Kernel requirements are not met, kernel version should be >= 5.19")
1605 'vtysh -c "conf" -c "nexthop-group resilience" -c "resilient buckets 64 idle-timer 128 unbalanced-timer 256" -c "nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.2 r1-eth2 onlink"'
1608 output
= net
["r1"].cmd('vtysh -c "show nexthop-group rib sharp"')
1609 output
= re
.findall(r
"Buckets", output
)
1611 verify_nexthop_group(185483878)
1612 assert len(output
) == 1, "Resilient NHG not created in zebra"
1615 def test_shutdown_check_stderr():
1617 net
= get_topogen().net
1619 # Skip if previous fatal error condition is raised
1620 if fatal_error
!= "":
1621 pytest
.skip(fatal_error
)
1623 print("\n\n** Verifying unexpected STDERR output from daemons")
1624 print("******************************************\n")
1626 if os
.environ
.get("TOPOTESTS_CHECK_STDERR") is None:
1628 "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
1630 pytest
.skip("Skipping test for Stderr output")
1632 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
1634 print("thisDir=" + thisDir
)
1636 net
["r1"].stopRouter()
1638 log
= net
["r1"].getStdErr("ripd")
1640 print("\nRIPd StdErr Log:\n" + log
)
1641 log
= net
["r1"].getStdErr("ripngd")
1643 print("\nRIPngd StdErr Log:\n" + log
)
1644 log
= net
["r1"].getStdErr("ospfd")
1646 print("\nOSPFd StdErr Log:\n" + log
)
1647 log
= net
["r1"].getStdErr("ospf6d")
1649 print("\nOSPF6d StdErr Log:\n" + log
)
1650 log
= net
["r1"].getStdErr("isisd")
1652 print("\nISISd StdErr Log:\n" + log
)
1653 log
= net
["r1"].getStdErr("bgpd")
1655 print("\nBGPd StdErr Log:\n" + log
)
1657 log
= net
["r1"].getStdErr("nhrpd")
1659 print("\nNHRPd StdErr Log:\n" + log
)
1661 log
= net
["r1"].getStdErr("pbrd")
1663 print("\nPBRd StdErr Log:\n" + log
)
1665 log
= net
["r1"].getStdErr("babeld")
1667 print("\nBABELd StdErr Log:\n" + log
)
1669 if net
["r1"].daemon_available("ldpd"):
1670 log
= net
["r1"].getStdErr("ldpd")
1672 print("\nLDPd StdErr Log:\n" + log
)
1673 log
= net
["r1"].getStdErr("zebra")
1675 print("\nZebra StdErr Log:\n" + log
)
1678 def test_shutdown_check_memleak():
1680 net
= get_topogen().net
1682 # Skip if previous fatal error condition is raised
1683 if fatal_error
!= "":
1684 pytest
.skip(fatal_error
)
1686 if os
.environ
.get("TOPOTESTS_CHECK_MEMLEAK") is None:
1688 "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n"
1690 pytest
.skip("Skipping test for memory leaks")
1692 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
1694 for i
in range(1, 2):
1695 net
["r%s" % i
].stopRouter()
1696 net
["r%s" % i
].report_memory_leaks(
1697 os
.environ
.get("TOPOTESTS_CHECK_MEMLEAK"), os
.path
.basename(__file__
)
1701 if __name__
== "__main__":
1702 # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
1703 # retval = pytest.main(["-s", "--tb=no"])
1704 retval
= pytest
.main(["-s"])