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