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