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