]>
Commit | Line | Data |
---|---|---|
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 | """ | |
26 | test_all_protocol_startup.py: Test of all protocols at same time | |
27 | ||
28 | """ | |
29 | ||
30 | import os | |
31 | import re | |
32 | import sys | |
4501fbca | 33 | import pytest |
6a57e103 | 34 | import glob |
4501fbca MW |
35 | from time import sleep |
36 | ||
37 | from mininet.topo import Topo | |
38 | from mininet.net import Mininet | |
39 | from mininet.node import Node, OVSSwitch, Host | |
40 | from mininet.log import setLogLevel, info | |
41 | from mininet.cli import CLI | |
42 | from mininet.link import Intf | |
43 | ||
44 | from functools import partial | |
45 | ||
2c647bcd DS |
46 | pytestmark = [ |
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 |
56 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
57 | from lib import topotest | |
58 | ||
59 | fatal_error = "" | |
60 | ||
61 | ||
62 | ##################################################### | |
63 | ## | |
64 | ## Network Topology Definition | |
65 | ## | |
66 | ##################################################### | |
67 | ||
701a0192 | 68 | |
4501fbca MW |
69 | class 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 |
96 | def 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 | ||
147 | def 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 | ||
157 | def 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 | ||
178 | def 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 | ||
234 | def 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 | ||
325 | def 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 |
415 | def 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 | 426 | def 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 | 457 | def 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 |
463 | def 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 |
612 | def 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 | ||
672 | def 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 | ||
739 | def 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 | ||
824 | def 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 | ||
890 | def 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 | |
907 | expected = open(refTableFile).read().rstrip() | |
908 | # Fix newlines (make them all the same) | |
701a0192 | 909 | expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) |
4501fbca MW |
910 | |
911 | # Actual output from router | |
701a0192 | 912 | actual = ( |
913 | net["r%s" % i] | |
914 | .cmd('vtysh -c "show ip bgp summary" 2> /dev/null') | |
915 | .rstrip() | |
916 | ) | |
4501fbca MW |
917 | # Mask out "using XXiXX bytes" portion. They are random... |
918 | actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual) | |
919 | # Mask out "using XiXXX KiB" portion. They are random... | |
920 | actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual) | |
08fa1af7 MW |
921 | # |
922 | # Remove extra summaries which exist with newer versions | |
923 | # | |
924 | # Remove summary lines (changed recently) | |
701a0192 | 925 | actual = re.sub(r"Total number.*", "", actual) |
926 | actual = re.sub(r"Displayed.*", "", actual) | |
08fa1af7 | 927 | # Remove IPv4 Unicast Summary (Title only) |
701a0192 | 928 | actual = re.sub(r"IPv4 Unicast Summary:", "", actual) |
08fa1af7 | 929 | # Remove IPv4 Multicast Summary (all of it) |
701a0192 | 930 | actual = re.sub(r"IPv4 Multicast Summary:", "", actual) |
931 | actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual) | |
08fa1af7 | 932 | # Remove IPv4 VPN Summary (all of it) |
701a0192 | 933 | actual = re.sub(r"IPv4 VPN Summary:", "", actual) |
934 | actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual) | |
08fa1af7 | 935 | # Remove IPv4 Encap Summary (all of it) |
701a0192 | 936 | actual = re.sub(r"IPv4 Encap Summary:", "", actual) |
937 | actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual) | |
08fa1af7 | 938 | # Remove Unknown Summary (all of it) |
701a0192 | 939 | actual = re.sub(r"Unknown Summary:", "", actual) |
940 | actual = re.sub(r"No Unknown neighbor is configured", "", actual) | |
8b2e59e9 | 941 | |
701a0192 | 942 | actual = re.sub(r"IPv4 labeled-unicast Summary:", "", actual) |
943 | actual = re.sub( | |
944 | r"No IPv4 labeled-unicast neighbor is configured", "", actual | |
945 | ) | |
8b2e59e9 | 946 | |
08fa1af7 MW |
947 | # Strip empty lines |
948 | actual = actual.lstrip() | |
949 | actual = actual.rstrip() | |
950 | # | |
4501fbca | 951 | # Fix newlines (make them all the same) |
701a0192 | 952 | actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) |
4501fbca MW |
953 | |
954 | # Generate Diff | |
701a0192 | 955 | diff = topotest.get_textdiff( |
956 | actual, | |
957 | expected, | |
17070436 | 958 | title1="actual SHOW IP BGP SUMMARY", |
701a0192 | 959 | title2="expected SHOW IP BGP SUMMARY", |
960 | ) | |
4501fbca MW |
961 | |
962 | # Empty string if it matches, otherwise diff contains unified diff | |
963 | if diff: | |
701a0192 | 964 | sys.stderr.write( |
965 | "r%s failed SHOW IP BGP SUMMARY check:\n%s\n" % (i, diff) | |
966 | ) | |
4501fbca MW |
967 | failures += 1 |
968 | else: | |
969 | print("r%s ok" % i) | |
970 | ||
701a0192 | 971 | assert failures == 0, "SHOW IP BGP SUMMARY failed for router r%s:\n%s" % ( |
972 | i, | |
973 | diff, | |
974 | ) | |
4501fbca | 975 | |
7e7fc73b MW |
976 | # Make sure that all daemons are running |
977 | for i in range(1, 2): | |
701a0192 | 978 | fatal_error = net["r%s" % i].checkRouterRunning() |
7e7fc73b MW |
979 | assert fatal_error == "", fatal_error |
980 | ||
622c4996 | 981 | # For debugging after starting FRR daemons, uncomment the next line |
4501fbca MW |
982 | # CLI(net) |
983 | ||
984 | ||
985 | def test_bgp_ipv6_summary(): | |
986 | global fatal_error | |
987 | global net | |
988 | ||
989 | # Skip if previous fatal error condition is raised | |
701a0192 | 990 | if fatal_error != "": |
4501fbca MW |
991 | pytest.skip(fatal_error) |
992 | ||
993 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
994 | ||
b2764f90 | 995 | print("\n\n** Verifying BGP IPv6 Summary") |
4501fbca MW |
996 | print("******************************************\n") |
997 | failures = 0 | |
998 | for i in range(1, 2): | |
701a0192 | 999 | refTableFile = "%s/r%s/show_bgp_ipv6_summary.ref" % (thisDir, i) |
4501fbca MW |
1000 | if os.path.isfile(refTableFile): |
1001 | # Read expected result from file | |
1002 | expected = open(refTableFile).read().rstrip() | |
1003 | # Fix newlines (make them all the same) | |
701a0192 | 1004 | expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) |
4501fbca MW |
1005 | |
1006 | # Actual output from router | |
701a0192 | 1007 | actual = ( |
1008 | net["r%s" % i] | |
1009 | .cmd('vtysh -c "show bgp ipv6 summary" 2> /dev/null') | |
1010 | .rstrip() | |
1011 | ) | |
4501fbca MW |
1012 | # Mask out "using XXiXX bytes" portion. They are random... |
1013 | actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual) | |
1014 | # Mask out "using XiXXX KiB" portion. They are random... | |
1015 | actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual) | |
08fa1af7 MW |
1016 | # |
1017 | # Remove extra summaries which exist with newer versions | |
1018 | # | |
1019 | # Remove summary lines (changed recently) | |
701a0192 | 1020 | actual = re.sub(r"Total number.*", "", actual) |
1021 | actual = re.sub(r"Displayed.*", "", actual) | |
08fa1af7 | 1022 | # Remove IPv4 Unicast Summary (Title only) |
701a0192 | 1023 | actual = re.sub(r"IPv6 Unicast Summary:", "", actual) |
08fa1af7 | 1024 | # Remove IPv4 Multicast Summary (all of it) |
701a0192 | 1025 | actual = re.sub(r"IPv6 Multicast Summary:", "", actual) |
1026 | actual = re.sub(r"No IPv6 Multicast neighbor is configured", "", actual) | |
08fa1af7 | 1027 | # Remove IPv4 VPN Summary (all of it) |
701a0192 | 1028 | actual = re.sub(r"IPv6 VPN Summary:", "", actual) |
1029 | actual = re.sub(r"No IPv6 VPN neighbor is configured", "", actual) | |
08fa1af7 | 1030 | # Remove IPv4 Encap Summary (all of it) |
701a0192 | 1031 | actual = re.sub(r"IPv6 Encap Summary:", "", actual) |
1032 | actual = re.sub(r"No IPv6 Encap neighbor is configured", "", actual) | |
08fa1af7 | 1033 | # Remove Unknown Summary (all of it) |
701a0192 | 1034 | actual = re.sub(r"Unknown Summary:", "", actual) |
1035 | actual = re.sub(r"No Unknown neighbor is configured", "", actual) | |
8b2e59e9 DS |
1036 | |
1037 | # Remove Labeled Unicast Summary (all of it) | |
701a0192 | 1038 | actual = re.sub(r"IPv6 labeled-unicast Summary:", "", actual) |
1039 | actual = re.sub( | |
1040 | r"No IPv6 labeled-unicast neighbor is configured", "", actual | |
1041 | ) | |
8b2e59e9 | 1042 | |
08fa1af7 MW |
1043 | # Strip empty lines |
1044 | actual = actual.lstrip() | |
1045 | actual = actual.rstrip() | |
1046 | # | |
4501fbca | 1047 | # Fix newlines (make them all the same) |
701a0192 | 1048 | actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) |
4501fbca MW |
1049 | |
1050 | # Generate Diff | |
701a0192 | 1051 | diff = topotest.get_textdiff( |
1052 | actual, | |
1053 | expected, | |
17070436 | 1054 | title1="actual SHOW BGP IPv6 SUMMARY", |
701a0192 | 1055 | title2="expected SHOW BGP IPv6 SUMMARY", |
1056 | ) | |
4501fbca MW |
1057 | |
1058 | # Empty string if it matches, otherwise diff contains unified diff | |
1059 | if diff: | |
701a0192 | 1060 | sys.stderr.write( |
1061 | "r%s failed SHOW BGP IPv6 SUMMARY check:\n%s\n" % (i, diff) | |
1062 | ) | |
4501fbca MW |
1063 | failures += 1 |
1064 | else: | |
1065 | print("r%s ok" % i) | |
1066 | ||
701a0192 | 1067 | assert failures == 0, "SHOW BGP IPv6 SUMMARY failed for router r%s:\n%s" % ( |
1068 | i, | |
1069 | diff, | |
1070 | ) | |
4501fbca | 1071 | |
7e7fc73b MW |
1072 | # Make sure that all daemons are running |
1073 | for i in range(1, 2): | |
701a0192 | 1074 | fatal_error = net["r%s" % i].checkRouterRunning() |
7e7fc73b MW |
1075 | assert fatal_error == "", fatal_error |
1076 | ||
622c4996 | 1077 | # For debugging after starting FRR daemons, uncomment the next line |
4501fbca MW |
1078 | # CLI(net) |
1079 | ||
9fa6ec14 | 1080 | |
dda33b6e DS |
1081 | def test_nht(): |
1082 | print("\n\n**** Test that nexthop tracking is at least nominally working ****\n") | |
1083 | ||
1084 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
1085 | ||
1086 | for i in range(1, 2): | |
1087 | nhtFile = "%s/r%s/ip_nht.ref" % (thisDir, i) | |
1088 | expected = open(nhtFile).read().rstrip() | |
1089 | expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) | |
1090 | ||
9fa6ec14 | 1091 | actual = net["r%s" % i].cmd('vtysh -c "show ip nht" 2> /dev/null').rstrip() |
dda33b6e DS |
1092 | actual = re.sub(r"fd [0-9][0-9]", "fd XX", actual) |
1093 | actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) | |
1094 | ||
9fa6ec14 | 1095 | diff = topotest.get_textdiff( |
1096 | actual, | |
1097 | expected, | |
1098 | title1="Actual `show ip nht`", | |
1099 | title2="Expected `show ip nht`", | |
1100 | ) | |
dda33b6e DS |
1101 | |
1102 | if diff: | |
1103 | assert 0, "r%s failed ip nht check:\n%s\n" % (i, diff) | |
1104 | else: | |
1105 | print("show ip nht is ok\n") | |
1106 | ||
1107 | nhtFile = "%s/r%s/ipv6_nht.ref" % (thisDir, i) | |
1108 | expected = open(nhtFile).read().rstrip() | |
1109 | expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) | |
1110 | ||
9fa6ec14 | 1111 | actual = net["r%s" % i].cmd('vtysh -c "show ipv6 nht" 2> /dev/null').rstrip() |
dda33b6e DS |
1112 | actual = re.sub(r"fd [0-9][0-9]", "fd XX", actual) |
1113 | actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) | |
1114 | ||
9fa6ec14 | 1115 | diff = topotest.get_textdiff( |
1116 | actual, | |
1117 | expected, | |
1118 | title1="Actual `show ip nht`", | |
1119 | title2="Expected `show ip nht`", | |
1120 | ) | |
dda33b6e DS |
1121 | |
1122 | if diff: | |
1123 | assert 0, "r%s failed ipv6 nht check:\n%s\n" % (i, diff) | |
1124 | else: | |
1125 | print("show ipv6 nht is ok\n") | |
4501fbca | 1126 | |
9fa6ec14 | 1127 | |
4501fbca MW |
1128 | def test_bgp_ipv4(): |
1129 | global fatal_error | |
1130 | global net | |
1131 | ||
1132 | # Skip if previous fatal error condition is raised | |
701a0192 | 1133 | if fatal_error != "": |
4501fbca MW |
1134 | pytest.skip(fatal_error) |
1135 | ||
1136 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
1137 | ||
b2764f90 | 1138 | print("\n\n** Verifying BGP IPv4") |
4501fbca | 1139 | print("******************************************\n") |
6a57e103 | 1140 | diffresult = {} |
4501fbca | 1141 | for i in range(1, 2): |
11761ab0 | 1142 | success = 0 |
701a0192 | 1143 | for refTableFile in glob.glob("%s/r%s/show_bgp_ipv4*.ref" % (thisDir, i)): |
11761ab0 MS |
1144 | if os.path.isfile(refTableFile): |
1145 | # Read expected result from file | |
1146 | expected = open(refTableFile).read().rstrip() | |
1147 | # Fix newlines (make them all the same) | |
701a0192 | 1148 | expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) |
11761ab0 MS |
1149 | |
1150 | # Actual output from router | |
701a0192 | 1151 | actual = ( |
1152 | net["r%s" % i].cmd('vtysh -c "show bgp ipv4" 2> /dev/null').rstrip() | |
1153 | ) | |
11761ab0 | 1154 | # Remove summary line (changed recently) |
701a0192 | 1155 | actual = re.sub(r"Total number.*", "", actual) |
1156 | actual = re.sub(r"Displayed.*", "", actual) | |
11761ab0 MS |
1157 | actual = actual.rstrip() |
1158 | # Fix newlines (make them all the same) | |
701a0192 | 1159 | actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) |
11761ab0 MS |
1160 | |
1161 | # Generate Diff | |
701a0192 | 1162 | diff = topotest.get_textdiff( |
1163 | actual, | |
1164 | expected, | |
11761ab0 | 1165 | title1="actual SHOW BGP IPv4", |
701a0192 | 1166 | title2="expected SHOW BGP IPv4", |
1167 | ) | |
11761ab0 MS |
1168 | |
1169 | # Empty string if it matches, otherwise diff contains unified diff | |
1170 | if diff: | |
1171 | diffresult[refTableFile] = diff | |
1172 | else: | |
1173 | success = 1 | |
1174 | print("template %s matched: r%s ok" % (refTableFile, i)) | |
1175 | break | |
1176 | ||
1177 | if not success: | |
701a0192 | 1178 | resultstr = "No template matched.\n" |
e7294b32 | 1179 | for f in diffresult.keys(): |
701a0192 | 1180 | resultstr += "template %s: r%s failed SHOW BGP IPv4 check:\n%s\n" % ( |
1181 | f, | |
1182 | i, | |
1183 | diffresult[f], | |
1184 | ) | |
11761ab0 | 1185 | raise AssertionError( |
701a0192 | 1186 | "SHOW BGP IPv4 failed for router r%s:\n%s" % (i, resultstr) |
1187 | ) | |
4501fbca | 1188 | |
7e7fc73b MW |
1189 | # Make sure that all daemons are running |
1190 | for i in range(1, 2): | |
701a0192 | 1191 | fatal_error = net["r%s" % i].checkRouterRunning() |
7e7fc73b MW |
1192 | assert fatal_error == "", fatal_error |
1193 | ||
622c4996 | 1194 | # For debugging after starting FRR daemons, uncomment the next line |
4501fbca MW |
1195 | # CLI(net) |
1196 | ||
1197 | ||
1198 | def test_bgp_ipv6(): | |
1199 | global fatal_error | |
1200 | global net | |
1201 | ||
1202 | # Skip if previous fatal error condition is raised | |
701a0192 | 1203 | if fatal_error != "": |
4501fbca MW |
1204 | pytest.skip(fatal_error) |
1205 | ||
1206 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
1207 | ||
b2764f90 | 1208 | print("\n\n** Verifying BGP IPv6") |
4501fbca | 1209 | print("******************************************\n") |
6a57e103 | 1210 | diffresult = {} |
4501fbca | 1211 | for i in range(1, 2): |
11761ab0 | 1212 | success = 0 |
701a0192 | 1213 | for refTableFile in glob.glob("%s/r%s/show_bgp_ipv6*.ref" % (thisDir, i)): |
11761ab0 MS |
1214 | if os.path.isfile(refTableFile): |
1215 | # Read expected result from file | |
1216 | expected = open(refTableFile).read().rstrip() | |
1217 | # Fix newlines (make them all the same) | |
701a0192 | 1218 | expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) |
11761ab0 MS |
1219 | |
1220 | # Actual output from router | |
701a0192 | 1221 | actual = ( |
1222 | net["r%s" % i].cmd('vtysh -c "show bgp ipv6" 2> /dev/null').rstrip() | |
1223 | ) | |
11761ab0 | 1224 | # Remove summary line (changed recently) |
701a0192 | 1225 | actual = re.sub(r"Total number.*", "", actual) |
1226 | actual = re.sub(r"Displayed.*", "", actual) | |
11761ab0 MS |
1227 | actual = actual.rstrip() |
1228 | # Fix newlines (make them all the same) | |
701a0192 | 1229 | actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) |
11761ab0 MS |
1230 | |
1231 | # Generate Diff | |
701a0192 | 1232 | diff = topotest.get_textdiff( |
1233 | actual, | |
1234 | expected, | |
11761ab0 | 1235 | title1="actual SHOW BGP IPv6", |
701a0192 | 1236 | title2="expected SHOW BGP IPv6", |
1237 | ) | |
11761ab0 MS |
1238 | |
1239 | # Empty string if it matches, otherwise diff contains unified diff | |
1240 | if diff: | |
1241 | diffresult[refTableFile] = diff | |
1242 | else: | |
1243 | success = 1 | |
1244 | print("template %s matched: r%s ok" % (refTableFile, i)) | |
1245 | ||
1246 | if not success: | |
701a0192 | 1247 | resultstr = "No template matched.\n" |
e7294b32 | 1248 | for f in diffresult.keys(): |
701a0192 | 1249 | resultstr += "template %s: r%s failed SHOW BGP IPv6 check:\n%s\n" % ( |
1250 | f, | |
1251 | i, | |
1252 | diffresult[f], | |
1253 | ) | |
11761ab0 | 1254 | raise AssertionError( |
701a0192 | 1255 | "SHOW BGP IPv6 failed for router r%s:\n%s" % (i, resultstr) |
1256 | ) | |
4501fbca | 1257 | |
7e7fc73b MW |
1258 | # Make sure that all daemons are running |
1259 | for i in range(1, 2): | |
701a0192 | 1260 | fatal_error = net["r%s" % i].checkRouterRunning() |
7e7fc73b MW |
1261 | assert fatal_error == "", fatal_error |
1262 | ||
622c4996 | 1263 | # For debugging after starting FRR daemons, uncomment the next line |
4501fbca MW |
1264 | # CLI(net) |
1265 | ||
701a0192 | 1266 | |
41fce07c DS |
1267 | def test_route_map(): |
1268 | global fatal_error | |
1269 | global net | |
1270 | ||
701a0192 | 1271 | if fatal_error != "": |
41fce07c DS |
1272 | pytest.skip(fatal_error) |
1273 | ||
1274 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
1275 | ||
1276 | print("\n\n** Verifying some basic routemap forward references\n") | |
1277 | print("*******************************************************\n") | |
1278 | failures = 0 | |
1279 | for i in range(1, 2): | |
701a0192 | 1280 | refroutemap = "%s/r%s/show_route_map.ref" % (thisDir, i) |
41fce07c DS |
1281 | if os.path.isfile(refroutemap): |
1282 | expected = open(refroutemap).read().rstrip() | |
701a0192 | 1283 | expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) |
41fce07c | 1284 | |
701a0192 | 1285 | actual = ( |
1286 | net["r%s" % i].cmd('vtysh -c "show route-map" 2> /dev/null').rstrip() | |
1287 | ) | |
1288 | actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) | |
41fce07c | 1289 | |
701a0192 | 1290 | diff = topotest.get_textdiff( |
1291 | actual, | |
1292 | expected, | |
1293 | title1="actual show route-map", | |
1294 | title2="expected show route-map", | |
1295 | ) | |
41fce07c DS |
1296 | |
1297 | if diff: | |
701a0192 | 1298 | sys.stderr.write( |
1299 | "r%s failed show route-map command Check:\n%s\n" % (i, diff) | |
1300 | ) | |
41fce07c DS |
1301 | failures += 1 |
1302 | else: | |
701a0192 | 1303 | print("r%s ok" % i) |
1304 | ||
1305 | assert ( | |
1306 | failures == 0 | |
1307 | ), "Show route-map command failed for router r%s:\n%s" % (i, diff) | |
4501fbca MW |
1308 | |
1309 | ||
887a232c SW |
1310 | def test_nexthop_groups_with_route_maps(): |
1311 | global fatal_error | |
1312 | global net | |
1313 | ||
1314 | # Skip if previous fatal error condition is raised | |
701a0192 | 1315 | if fatal_error != "": |
887a232c SW |
1316 | pytest.skip(fatal_error) |
1317 | ||
1318 | print("\n\n** Verifying Nexthop Groups With Route-Maps") | |
1319 | print("******************************************\n") | |
1320 | ||
1321 | ### Nexthop Group With Route-Map Tests | |
1322 | ||
1323 | # Create a lib nexthop-group | |
701a0192 | 1324 | net["r1"].cmd( |
1325 | 'vtysh -c "c t" -c "nexthop-group test" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"' | |
1326 | ) | |
887a232c SW |
1327 | |
1328 | ## Route-Map Proto Source | |
1329 | ||
1330 | route_str = "2.2.2.1" | |
1331 | src_str = "192.168.0.1" | |
1332 | ||
701a0192 | 1333 | net["r1"].cmd( |
1334 | 'vtysh -c "c t" -c "route-map NH-SRC permit 111" -c "set src %s"' % src_str | |
1335 | ) | |
887a232c SW |
1336 | net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NH-SRC"') |
1337 | ||
1338 | net["r1"].cmd('vtysh -c "sharp install routes %s nexthop-group test 1"' % route_str) | |
1339 | ||
1340 | verify_route_nexthop_group("%s/32" % route_str) | |
1341 | ||
1342 | # Only a valid test on linux using nexthop objects | |
1343 | if sys.platform.startswith("linux"): | |
701a0192 | 1344 | output = net["r1"].cmd("ip route show %s/32" % route_str) |
887a232c | 1345 | match = re.search(r"src %s" % src_str, output) |
701a0192 | 1346 | assert match is not None, "Route %s/32 not installed with src %s" % ( |
1347 | route_str, | |
1348 | src_str, | |
1349 | ) | |
887a232c SW |
1350 | |
1351 | # Remove NHG routes and route-map | |
1352 | net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % route_str) | |
1353 | net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NH-SRC"') | |
701a0192 | 1354 | net["r1"].cmd( |
1355 | 'vtysh -c "c t" -c "no route-map NH-SRC permit 111" -c "set src %s"' % src_str | |
1356 | ) | |
887a232c SW |
1357 | net["r1"].cmd('vtysh -c "c t" -c "no route-map NH-SRC"') |
1358 | ||
1359 | ## Route-Map Deny/Permit with same nexthop group | |
1360 | ||
1361 | permit_route_str = "3.3.3.1" | |
1362 | deny_route_str = "3.3.3.2" | |
1363 | ||
701a0192 | 1364 | net["r1"].cmd( |
1365 | 'vtysh -c "c t" -c "ip prefix-list NOPE seq 5 permit %s/32"' % permit_route_str | |
1366 | ) | |
1367 | net["r1"].cmd( | |
1368 | 'vtysh -c "c t" -c "route-map NOPE permit 111" -c "match ip address prefix-list NOPE"' | |
1369 | ) | |
887a232c SW |
1370 | net["r1"].cmd('vtysh -c "c t" -c "route-map NOPE deny 222"') |
1371 | net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NOPE"') | |
1372 | ||
1373 | # This route should be permitted | |
701a0192 | 1374 | net["r1"].cmd( |
1375 | 'vtysh -c "sharp install routes %s nexthop-group test 1"' % permit_route_str | |
1376 | ) | |
887a232c SW |
1377 | |
1378 | verify_route_nexthop_group("%s/32" % permit_route_str) | |
1379 | ||
1380 | # This route should be denied | |
701a0192 | 1381 | net["r1"].cmd( |
1382 | 'vtysh -c "sharp install routes %s nexthop-group test 1"' % deny_route_str | |
1383 | ) | |
887a232c SW |
1384 | |
1385 | nhg_id = route_get_nhg_id(deny_route_str) | |
1386 | output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id) | |
1387 | ||
1388 | match = re.search(r"Valid", output) | |
1389 | assert match is None, "Nexthop Group ID=%d should not be marked Valid" % nhg_id | |
1390 | ||
1391 | match = re.search(r"Installed", output) | |
1392 | assert match is None, "Nexthop Group ID=%d should not be marked Installed" % nhg_id | |
1393 | ||
1394 | # Remove NHG routes and route-map | |
1395 | net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % permit_route_str) | |
1396 | net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % deny_route_str) | |
1397 | net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NOPE"') | |
1398 | net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE permit 111"') | |
1399 | net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE deny 222"') | |
1400 | net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE"') | |
701a0192 | 1401 | net["r1"].cmd( |
1402 | 'vtysh -c "c t" -c "no ip prefix-list NOPE seq 5 permit %s/32"' | |
1403 | % permit_route_str | |
1404 | ) | |
1405 | ||
887a232c | 1406 | |
8f4d7212 SW |
1407 | def test_nexthop_group_replace(): |
1408 | global fatal_error | |
1409 | global net | |
1410 | ||
1411 | # Skip if previous fatal error condition is raised | |
701a0192 | 1412 | if fatal_error != "": |
8f4d7212 SW |
1413 | pytest.skip(fatal_error) |
1414 | ||
1415 | print("\n\n** Verifying Nexthop Groups") | |
1416 | print("******************************************\n") | |
1417 | ||
1418 | ### Nexthop Group Tests | |
1419 | ||
1420 | ## 2-Way ECMP Directly Connected | |
1421 | ||
701a0192 | 1422 | net["r1"].cmd( |
1423 | '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"' | |
1424 | ) | |
8f4d7212 SW |
1425 | |
1426 | # Create with sharpd using nexthop-group | |
1427 | net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.1 nexthop-group replace 1"') | |
1428 | ||
1429 | verify_route_nexthop_group("3.3.3.1/32") | |
1430 | ||
1431 | # Change the nexthop group | |
701a0192 | 1432 | net["r1"].cmd( |
1433 | '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"' | |
1434 | ) | |
8f4d7212 SW |
1435 | |
1436 | # Verify it updated. We can just check install and ecmp count here. | |
1437 | verify_route_nexthop_group("3.3.3.1/32", False, 3) | |
1438 | ||
701a0192 | 1439 | |
4501fbca MW |
1440 | def test_mpls_interfaces(): |
1441 | global fatal_error | |
1442 | global net | |
1443 | ||
1444 | # Skip if previous fatal error condition is raised | |
701a0192 | 1445 | if fatal_error != "": |
4501fbca MW |
1446 | pytest.skip(fatal_error) |
1447 | ||
1448 | # Skip if no LDP installed or old kernel | |
701a0192 | 1449 | if net["r1"].daemon_available("ldpd") == False: |
4501fbca MW |
1450 | pytest.skip("No MPLS or kernel < 4.5") |
1451 | ||
1452 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
1453 | ||
b2764f90 | 1454 | print("\n\n** Verifying MPLS Interfaces") |
4501fbca MW |
1455 | print("******************************************\n") |
1456 | failures = 0 | |
1457 | for i in range(1, 2): | |
701a0192 | 1458 | refTableFile = "%s/r%s/show_mpls_ldp_interface.ref" % (thisDir, i) |
4501fbca MW |
1459 | if os.path.isfile(refTableFile): |
1460 | # Read expected result from file | |
1461 | expected = open(refTableFile).read().rstrip() | |
1462 | # Fix newlines (make them all the same) | |
701a0192 | 1463 | expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) |
4501fbca MW |
1464 | |
1465 | # Actual output from router | |
701a0192 | 1466 | actual = ( |
1467 | net["r%s" % i] | |
1468 | .cmd('vtysh -c "show mpls ldp interface" 2> /dev/null') | |
1469 | .rstrip() | |
1470 | ) | |
4501fbca MW |
1471 | # Mask out Timer in Uptime |
1472 | actual = re.sub(r" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual) | |
1473 | # Fix newlines (make them all the same) | |
701a0192 | 1474 | actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) |
4501fbca MW |
1475 | |
1476 | # Generate Diff | |
701a0192 | 1477 | diff = topotest.get_textdiff( |
1478 | actual, | |
1479 | expected, | |
17070436 | 1480 | title1="actual MPLS LDP interface status", |
701a0192 | 1481 | title2="expected MPLS LDP interface status", |
1482 | ) | |
4501fbca MW |
1483 | |
1484 | # Empty string if it matches, otherwise diff contains unified diff | |
1485 | if diff: | |
701a0192 | 1486 | sys.stderr.write( |
1487 | "r%s failed MPLS LDP Interface status Check:\n%s\n" % (i, diff) | |
1488 | ) | |
4501fbca MW |
1489 | failures += 1 |
1490 | else: | |
1491 | print("r%s ok" % i) | |
1492 | ||
701a0192 | 1493 | if failures > 0: |
4501fbca MW |
1494 | fatal_error = "MPLS LDP Interface status failed" |
1495 | ||
701a0192 | 1496 | assert ( |
1497 | failures == 0 | |
1498 | ), "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff) | |
4501fbca | 1499 | |
7e7fc73b MW |
1500 | # Make sure that all daemons are running |
1501 | for i in range(1, 2): | |
701a0192 | 1502 | fatal_error = net["r%s" % i].checkRouterRunning() |
7e7fc73b MW |
1503 | assert fatal_error == "", fatal_error |
1504 | ||
622c4996 | 1505 | # For debugging after starting FRR daemons, uncomment the next line |
4501fbca MW |
1506 | # CLI(net) |
1507 | ||
1508 | ||
1509 | def test_shutdown_check_stderr(): | |
1510 | global fatal_error | |
1511 | global net | |
1512 | ||
1513 | # Skip if previous fatal error condition is raised | |
701a0192 | 1514 | if fatal_error != "": |
4501fbca MW |
1515 | pytest.skip(fatal_error) |
1516 | ||
b2764f90 | 1517 | print("\n\n** Verifying unexpected STDERR output from daemons") |
4501fbca MW |
1518 | print("******************************************\n") |
1519 | ||
701a0192 | 1520 | if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: |
1521 | print( | |
1522 | "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n" | |
1523 | ) | |
1524 | pytest.skip("Skipping test for Stderr output") | |
4501fbca MW |
1525 | |
1526 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
1527 | ||
50c40bde MW |
1528 | print("thisDir=" + thisDir) |
1529 | ||
701a0192 | 1530 | net["r1"].stopRouter() |
4501fbca | 1531 | |
701a0192 | 1532 | log = net["r1"].getStdErr("ripd") |
8e957dbb MW |
1533 | if log: |
1534 | print("\nRIPd StdErr Log:\n" + log) | |
701a0192 | 1535 | log = net["r1"].getStdErr("ripngd") |
8e957dbb MW |
1536 | if log: |
1537 | print("\nRIPngd StdErr Log:\n" + log) | |
701a0192 | 1538 | log = net["r1"].getStdErr("ospfd") |
8e957dbb MW |
1539 | if log: |
1540 | print("\nOSPFd StdErr Log:\n" + log) | |
701a0192 | 1541 | log = net["r1"].getStdErr("ospf6d") |
8e957dbb MW |
1542 | if log: |
1543 | print("\nOSPF6d StdErr Log:\n" + log) | |
701a0192 | 1544 | log = net["r1"].getStdErr("isisd") |
8e957dbb MW |
1545 | if log: |
1546 | print("\nISISd StdErr Log:\n" + log) | |
701a0192 | 1547 | log = net["r1"].getStdErr("bgpd") |
8e957dbb MW |
1548 | if log: |
1549 | print("\nBGPd StdErr Log:\n" + log) | |
af39fbe7 | 1550 | |
701a0192 | 1551 | log = net["r1"].getStdErr("nhrpd") |
af39fbe7 MS |
1552 | if log: |
1553 | print("\nNHRPd StdErr Log:\n" + log) | |
1554 | ||
701a0192 | 1555 | log = net["r1"].getStdErr("pbrd") |
af39fbe7 MS |
1556 | if log: |
1557 | print("\nPBRd StdErr Log:\n" + log) | |
1558 | ||
701a0192 | 1559 | log = net["r1"].getStdErr("babeld") |
af39fbe7 MS |
1560 | if log: |
1561 | print("\nBABELd StdErr Log:\n" + log) | |
1562 | ||
701a0192 | 1563 | if net["r1"].daemon_available("ldpd"): |
1564 | log = net["r1"].getStdErr("ldpd") | |
8e957dbb MW |
1565 | if log: |
1566 | print("\nLDPd StdErr Log:\n" + log) | |
701a0192 | 1567 | log = net["r1"].getStdErr("zebra") |
8e957dbb MW |
1568 | if log: |
1569 | print("\nZebra StdErr Log:\n" + log) | |
4501fbca MW |
1570 | |
1571 | ||
50c40bde MW |
1572 | def test_shutdown_check_memleak(): |
1573 | global fatal_error | |
1574 | global net | |
1575 | ||
1576 | # Skip if previous fatal error condition is raised | |
701a0192 | 1577 | if fatal_error != "": |
50c40bde MW |
1578 | pytest.skip(fatal_error) |
1579 | ||
701a0192 | 1580 | if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None: |
1581 | print( | |
1582 | "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n" | |
1583 | ) | |
1584 | pytest.skip("Skipping test for memory leaks") | |
1585 | ||
50c40bde MW |
1586 | thisDir = os.path.dirname(os.path.realpath(__file__)) |
1587 | ||
1588 | for i in range(1, 2): | |
701a0192 | 1589 | net["r%s" % i].stopRouter() |
1590 | net["r%s" % i].report_memory_leaks( | |
1591 | os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__) | |
1592 | ) | |
50c40bde MW |
1593 | |
1594 | ||
701a0192 | 1595 | if __name__ == "__main__": |
4501fbca | 1596 | |
701a0192 | 1597 | setLogLevel("info") |
4501fbca MW |
1598 | # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli |
1599 | # retval = pytest.main(["-s", "--tb=no"]) | |
1600 | retval = pytest.main(["-s"]) | |
1601 | sys.exit(retval) |