4 # test_ospf6_topo1_vrf.py
5 # Part of NetDEF Topology Tests
7 # Copyright (c) 2021 by Niral Networks, Inc. ("Niral Networks")
8 # Used Copyright (c) 2016 by Network Device Education Foundation,
9 # Inc. ("NetDEF") in this file.
11 # Permission to use, copy, modify, and/or distribute this software
12 # for any purpose with or without fee is hereby granted, provided
13 # that the above copyright notice and this permission notice appear
16 # THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
17 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
19 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
20 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
21 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
22 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
27 test_ospf6_topo1_vrf.py:
30 SW1 - Stub Net 1 SW2 - Stub Net 2 \
31 fc00:1:1:1::/64 fc00:2:2:2::/64 \
32 \___________________/ \___________________/ |
36 +---------+---------+ +---------+---------+ |
38 | FRRouting | | FRRouting | |
39 | Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | |
40 +---------+---------+ +---------+---------+ |
42 \______ ___________/ OSPFv3
48 ~~ fc00:A:A:A::/64 ~~ |
51 | ::3 | SW3 - Stub Net 3 |
52 +---------+---------+ /-+ fc00:3:3:3::/64 |
54 | FRRouting +--/ \---- /
55 | Rtr-ID: 10.0.0.3 | ::3 ___________/
56 +---------+---------+ \
62 ~~ fc00:B:B:B::/64 ~~ \
63 ~~~~~~~~~~~~~~~~~~ OSPFv3
66 +---------+---------+ /---- |
67 | R4 | | SW4 - Stub Net 4 |
68 | FRRouting +------+ fc00:4:4:4::/64 |
69 | Rtr-ID: 10.0.0.4 | ::4 | /
70 +-------------------+ \---- /
79 from functools
import partial
82 # Save the Current Working Directory to find configuration files later.
83 CWD
= os
.path
.dirname(os
.path
.realpath(__file__
))
84 sys
.path
.append(os
.path
.join(CWD
, "../"))
86 # pylint: disable=C0413
87 # Import topogen and topotest helpers
88 from lib
import topotest
89 from lib
.topogen
import Topogen
, TopoRouter
, get_topogen
90 from lib
.topolog
import logger
91 from lib
.topotest
import iproute2_is_vrf_capable
92 from lib
.common_config
import required_linux_kernel_version
95 pytestmark
= [pytest
.mark
.ospfd
]
102 for routern
in range(1, 5):
103 tgen
.add_router("r{}".format(routern
))
106 # Wire up the switches and routers
107 # Note that we specify the link names so we match the config files
110 # Create a empty network for router 1
111 switch
= tgen
.add_switch("s1")
112 switch
.add_link(tgen
.gears
["r1"], nodeif
="r1-stubnet")
114 # Create a empty network for router 2
115 switch
= tgen
.add_switch("s2")
116 switch
.add_link(tgen
.gears
["r2"], nodeif
="r2-stubnet")
118 # Create a empty network for router 3
119 switch
= tgen
.add_switch("s3")
120 switch
.add_link(tgen
.gears
["r3"], nodeif
="r3-stubnet")
122 # Create a empty network for router 4
123 switch
= tgen
.add_switch("s4")
124 switch
.add_link(tgen
.gears
["r4"], nodeif
="r4-stubnet")
126 # Interconnect routers 1, 2, and 3
127 switch
= tgen
.add_switch("s5")
128 switch
.add_link(tgen
.gears
["r1"], nodeif
="r1-sw5")
129 switch
.add_link(tgen
.gears
["r2"], nodeif
="r2-sw5")
130 switch
.add_link(tgen
.gears
["r3"], nodeif
="r3-sw5")
132 # Interconnect routers 3 and 4
133 switch
= tgen
.add_switch("s6")
134 switch
.add_link(tgen
.gears
["r3"], nodeif
="r3-sw6")
135 switch
.add_link(tgen
.gears
["r4"], nodeif
="r4-sw6")
138 #####################################################
142 #####################################################
145 def setup_module(mod
):
146 "Sets up the pytest environment"
148 # Required linux kernel version for this suite to run.
149 result
= required_linux_kernel_version("5.0")
150 if result
is not True:
151 pytest
.skip("Kernel requirements are not met")
153 tgen
= Topogen(build_topo
, mod
.__name
__)
154 tgen
.start_topology()
156 logger
.info("** %s: Setup Topology" % mod
.__name
__)
157 logger
.info("******************************************")
159 # For debugging after starting net, but before starting FRR,
160 # uncomment the next line
163 logger
.info("Testing with VRF Lite support")
166 "ip link add {0}-cust1 type vrf table 1001",
167 "ip link add loop1 type dummy",
168 "ip link set {0}-stubnet master {0}-cust1",
171 cmds1
= ["ip link set {0}-sw5 master {0}-cust1"]
173 cmds2
= ["ip link set {0}-sw6 master {0}-cust1"]
175 # For all registered routers, load the zebra configuration file
176 for rname
, router
in tgen
.routers().items():
177 # create VRF rx-cust1 and link rx-eth0 to rx-cust1
179 output
= tgen
.net
[rname
].cmd(cmd
.format(rname
))
180 if rname
== "r1" or rname
== "r2" or rname
== "r3":
182 output
= tgen
.net
[rname
].cmd(cmd
.format(rname
))
183 if rname
== "r3" or rname
== "r4":
185 output
= tgen
.net
[rname
].cmd(cmd
.format(rname
))
187 for rname
, router
in tgen
.routers().items():
189 TopoRouter
.RD_ZEBRA
, os
.path
.join(CWD
, "{}/zebra.conf".format(rname
))
192 TopoRouter
.RD_OSPF6
, os
.path
.join(CWD
, "{}/ospf6d.conf".format(rname
))
195 # Initialize all routers.
198 # For debugging after starting FRR daemons, uncomment the next line
202 def teardown_module(mod
):
203 "Teardown the pytest environment"
208 def test_wait_protocol_convergence():
209 "Wait for OSPFv3 to converge"
211 if tgen
.routers_have_failure():
212 pytest
.skip(tgen
.errors
)
214 logger
.info("waiting for protocols to converge")
216 def expect_neighbor_full(router
, neighbor
):
217 "Wait until OSPFv3 convergence."
218 logger
.info("waiting OSPFv3 router '{}'".format(router
))
220 topotest
.router_json_cmp
,
222 "show ipv6 ospf6 vrf {0}-cust1 neighbor json".format(router
),
223 {"neighbors": [{"neighborId": neighbor
, "state": "Full"}]},
225 _
, result
= topotest
.run_and_expect(test_func
, None, count
=130, wait
=1)
226 assertmsg
= '"{}" convergence failure'.format(router
)
227 assert result
is None, assertmsg
229 expect_neighbor_full("r1", "10.0.0.2")
230 expect_neighbor_full("r1", "10.0.0.3")
232 expect_neighbor_full("r2", "10.0.0.1")
233 expect_neighbor_full("r2", "10.0.0.3")
235 expect_neighbor_full("r3", "10.0.0.1")
236 expect_neighbor_full("r3", "10.0.0.2")
237 expect_neighbor_full("r3", "10.0.0.4")
240 def compare_show_ipv6_vrf(rname
, expected
):
242 Calls 'show ipv6 route' for router `rname` and compare the obtained
243 result with the expected output.
247 # Use the vtysh output, with some masking to make comparison easy
248 vrf_name
= "{0}-cust1".format(rname
)
249 current
= topotest
.ip6_route_zebra(tgen
.gears
[rname
], vrf_name
)
251 # Use just the 'O'spf lines of the output
253 for line
in current
.splitlines():
254 if re
.match("^O", line
):
257 current
= "\n".join(linearr
)
259 return topotest
.difflines(
260 topotest
.normalize_text(current
),
261 topotest
.normalize_text(expected
),
262 title1
="Current output",
263 title2
="Expected output",
267 def test_ospfv3_routingTable():
270 if tgen
.routers_have_failure():
271 pytest
.skip("skipped because of router(s) failure")
273 # For debugging, uncomment the next line
275 # Verify OSPFv3 Routing Table
276 for router
, rnode
in tgen
.routers().items():
277 logger
.info('Waiting for router "%s" convergence', router
)
279 # Load expected results from the command
280 reffile
= os
.path
.join(CWD
, "{}/show_ipv6_vrf_route.ref".format(router
))
281 expected
= open(reffile
).read()
283 # Run test function until we get an result. Wait at most 60 seconds.
284 test_func
= partial(compare_show_ipv6_vrf
, router
, expected
)
285 result
, diff
= topotest
.run_and_expect(test_func
, "", count
=120, wait
=0.5)
286 assert result
, "OSPFv3 did not converge on {}:\n{}".format(router
, diff
)
289 def test_linux_ipv6_kernel_routingTable():
291 # Required linux kernel version for this suite to run.
292 result
= required_linux_kernel_version("4.15")
293 if result
is not True:
294 pytest
.skip("Kernel requirements are not met")
296 # iproute2 needs to support VRFs for this suite to run.
297 if not iproute2_is_vrf_capable():
298 pytest
.skip("Installed iproute2 version does not support VRFs")
302 if tgen
.routers_have_failure():
303 pytest
.skip("skipped because of router(s) failure")
305 # Verify Linux Kernel Routing Table
306 logger
.info("Verifying Linux IPv6 Kernel Routing Table")
310 # Get a list of all current link-local addresses first as they change for
311 # each run and we need to translate them
313 for i
in range(1, 5):
314 linklocals
+= tgen
.net
["r{}".format(i
)].get_ipv6_linklocal()
316 # Now compare the routing tables (after substituting link-local addresses)
318 for i
in range(1, 5):
319 # Actual output from router
321 tgen
.gears
["r{}".format(i
)]
322 .run("ip -6 route show vrf r{}-cust1".format(i
))
326 refTableFile
= os
.path
.join(CWD
, "r{}/ip_6_address.nhg.ref".format(i
))
328 refTableFile
= os
.path
.join(CWD
, "r{}/ip_6_address.ref".format(i
))
330 if os
.path
.isfile(refTableFile
):
331 expected
= open(refTableFile
).read().rstrip()
332 # Fix newlines (make them all the same)
333 expected
= ("\n".join(expected
.splitlines())).splitlines(1)
335 # Mask out Link-Local mac addresses
336 for ll
in linklocals
:
337 actual
= actual
.replace(ll
[1], "fe80::__(%s)__" % ll
[0])
338 # Mask out protocol name or number
339 actual
= re
.sub(r
"[ ]+proto [0-9a-z]+ +", " proto XXXX ", actual
)
340 actual
= re
.sub(r
"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual
)
341 # Remove ff00::/8 routes (seen on some kernels - not from FRR)
342 actual
= re
.sub(r
"ff00::/8.*", "", actual
)
345 actual
= actual
.lstrip()
346 actual
= actual
.rstrip()
347 actual
= re
.sub(r
" +", " ", actual
)
350 for line
in sorted(actual
.splitlines()):
351 if line
.startswith("fe80::/64 ") or line
.startswith(
352 "unreachable fe80::/64 "
355 if "anycast" in line
:
357 if "multicast" in line
:
359 filtered_lines
.append(line
)
360 actual
= "\n".join(filtered_lines
).splitlines(1)
363 # logger.info("Router r%s table" % i)
364 # for line in actual:
365 # logger.info(line.rstrip())
368 diff
= topotest
.get_textdiff(
371 title1
="actual OSPFv3 IPv6 routing table",
372 title2
="expected OSPFv3 IPv6 routing table",
375 # Empty string if it matches, otherwise diff contains unified diff
378 "r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n"
383 logger
.info("r%s ok" % i
)
385 assert failures
== 0, (
386 "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s"
391 def test_ospfv3_routingTable_write_multiplier():
394 if tgen
.routers_have_failure():
395 pytest
.skip("skipped because of router(s) failure")
397 # For debugging, uncomment the next line
399 # Modify R1 write muliplier and reset the interfaces
400 r1
= tgen
.gears
["r1"]
402 r1
.vtysh_cmd("conf t\nrouter ospf6 vrf r1-cust1 \n write-multiplier 100")
403 r1
.vtysh_cmd("clear ipv6 ospf interface r1-stubnet")
404 r1
.vtysh_cmd("clear ipv6 ospf interface r1-sw5")
406 # Verify OSPFv3 Routing Table
407 for router
, rnode
in tgen
.routers().items():
408 logger
.info('Waiting for router "%s" convergence', router
)
410 # Load expected results from the command
411 reffile
= os
.path
.join(CWD
, "{}/show_ipv6_vrf_route.ref".format(router
))
412 expected
= open(reffile
).read()
414 # Run test function until we get an result. Wait at most 60 seconds.
415 test_func
= partial(compare_show_ipv6_vrf
, router
, expected
)
416 result
, diff
= topotest
.run_and_expect(test_func
, "", count
=120, wait
=0.5)
417 assert result
, "OSPFv3 did not converge on {}:\n{}".format(router
, diff
)
420 def test_shutdown_check_stderr():
424 if tgen
.routers_have_failure():
425 pytest
.skip("skipped because of router(s) failure")
427 if os
.environ
.get("TOPOTESTS_CHECK_STDERR") is None:
429 "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
431 pytest
.skip("Skipping test for Stderr output")
435 logger
.info("\n\n** Verifying unexpected STDERR output from daemons")
436 logger
.info("******************************************")
438 for i
in range(1, 5):
439 net
["r%s" % i
].stopRouter()
440 log
= net
["r%s" % i
].getStdErr("ospf6d")
442 logger
.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i
, log
))
443 log
= net
["r%s" % i
].getStdErr("zebra")
445 logger
.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i
, log
))
448 def test_shutdown_check_memleak():
449 "Run the memory leak test and report results."
451 if os
.environ
.get("TOPOTESTS_CHECK_MEMLEAK") is None:
453 "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)"
455 pytest
.skip("Skipping test for memory leaks")
461 for i
in range(1, 5):
462 net
["r%s" % i
].stopRouter()
463 net
["r%s" % i
].report_memory_leaks(
464 os
.environ
.get("TOPOTESTS_CHECK_MEMLEAK"), os
.path
.basename(__file__
)
468 if __name__
== "__main__":
470 # To suppress tracebacks, either use the following pytest call or
471 # add "--tb=no" to cli
472 # retval = pytest.main(["-s", "--tb=no"])
474 retval
= pytest
.main(["-s"])