]>
Commit | Line | Data |
---|---|---|
35490676 | 1 | #!/usr/bin/env python |
acddc0ed | 2 | # SPDX-License-Identifier: ISC |
35490676 PG |
3 | |
4 | # | |
f2f5c986 | 5 | # test_ospf_netns_vrf.py |
35490676 PG |
6 | # Part of NetDEF Topology Tests |
7 | # | |
8 | # Copyright (c) 2017 by | |
9 | # Network Device Education Foundation, Inc. ("NetDEF") | |
10 | # | |
35490676 PG |
11 | |
12 | """ | |
f2f5c986 | 13 | test_ospf_netns_vrf.py: Test OSPF with Network Namespace VRFs. |
35490676 PG |
14 | """ |
15 | ||
16 | import os | |
35490676 PG |
17 | import sys |
18 | from functools import partial | |
19 | import pytest | |
20 | ||
21 | # Save the Current Working Directory to find configuration files. | |
22 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
787e7624 | 23 | sys.path.append(os.path.join(CWD, "../")) |
35490676 PG |
24 | |
25 | # pylint: disable=C0413 | |
26 | # Import topogen and topotest helpers | |
27 | from lib import topotest | |
28 | from lib.topogen import Topogen, TopoRouter, get_topogen | |
29 | from lib.topolog import logger | |
30 | ||
31 | # Required to instantiate the topology builder class. | |
35490676 | 32 | |
3dedee4f | 33 | pytestmark = [pytest.mark.ospfd] |
787e7624 | 34 | |
5980ad0a | 35 | |
e82b531d CH |
36 | def build_topo(tgen): |
37 | "Build function" | |
787e7624 | 38 | |
e82b531d CH |
39 | # Create 3 routers |
40 | for routern in range(1, 4): | |
41 | tgen.add_router("r{}".format(routern)) | |
35490676 | 42 | |
e82b531d CH |
43 | # Create a empty network for router 1 |
44 | switch = tgen.add_switch("s1") | |
45 | switch.add_link(tgen.gears["r1"]) | |
35490676 | 46 | |
e82b531d CH |
47 | # Create a empty network for router 2 |
48 | switch = tgen.add_switch("s2") | |
49 | switch.add_link(tgen.gears["r2"]) | |
35490676 | 50 | |
e82b531d CH |
51 | # Interconect router 1, 2 and 3 |
52 | switch = tgen.add_switch("s3") | |
53 | switch.add_link(tgen.gears["r1"]) | |
54 | switch.add_link(tgen.gears["r2"]) | |
55 | switch.add_link(tgen.gears["r3"]) | |
35490676 | 56 | |
e82b531d CH |
57 | # Create empty netowrk for router3 |
58 | switch = tgen.add_switch("s4") | |
59 | switch.add_link(tgen.gears["r3"]) | |
35490676 PG |
60 | |
61 | ||
62 | def setup_module(mod): | |
63 | "Sets up the pytest environment" | |
e82b531d | 64 | tgen = Topogen(build_topo, mod.__name__) |
35490676 PG |
65 | tgen.start_topology() |
66 | ||
67 | router_list = tgen.routers() | |
68 | ||
69 | # check for zebra capability | |
e5f0ed14 | 70 | for rname, router in router_list.items(): |
787e7624 | 71 | if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False: |
72 | return pytest.skip( | |
73 | "Skipping OSPF VRF NETNS feature. VRF NETNS backend not available on FRR" | |
74 | ) | |
75 | ||
76 | if os.system("ip netns list") != 0: | |
77 | return pytest.skip( | |
78 | "Skipping OSPF VRF NETNS Test. NETNS not available on System" | |
79 | ) | |
35490676 | 80 | |
787e7624 | 81 | logger.info("Testing with VRF Namespace support") |
35490676 | 82 | |
e5f0ed14 | 83 | for rname, router in router_list.items(): |
8db751b8 CH |
84 | # create VRF rx-ospf-cust1 and link rx-eth{0,1} to rx-ospf-cust1 |
85 | ns = "{}-ospf-cust1".format(rname) | |
86 | router.net.add_netns(ns) | |
87 | router.net.set_intf_netns(rname + "-eth0", ns, up=True) | |
88 | router.net.set_intf_netns(rname + "-eth1", ns, up=True) | |
35490676 PG |
89 | |
90 | router.load_config( | |
91 | TopoRouter.RD_ZEBRA, | |
787e7624 | 92 | os.path.join(CWD, "{}/zebra.conf".format(rname)), |
93 | "--vrfwnetns", | |
35490676 PG |
94 | ) |
95 | router.load_config( | |
787e7624 | 96 | TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) |
35490676 PG |
97 | ) |
98 | ||
99 | # Initialize all routers. | |
100 | tgen.start_router() | |
9aecc71d | 101 | for router in router_list.values(): |
787e7624 | 102 | if router.has_version("<", "4.0"): |
103 | tgen.set_error("unsupported version") | |
9aecc71d | 104 | |
35490676 PG |
105 | |
106 | def teardown_module(mod): | |
107 | "Teardown the pytest environment" | |
108 | tgen = get_topogen() | |
109 | ||
8db751b8 | 110 | # Move interfaces out of vrf namespace and delete the namespace |
35490676 | 111 | router_list = tgen.routers() |
e5f0ed14 | 112 | for rname, router in router_list.items(): |
8db751b8 CH |
113 | tgen.net[rname].reset_intf_netns(rname + "-eth0") |
114 | tgen.net[rname].reset_intf_netns(rname + "-eth1") | |
115 | tgen.net[rname].delete_netns(rname + "-ospf-cust1") | |
35490676 PG |
116 | tgen.stop_topology() |
117 | ||
787e7624 | 118 | |
35490676 | 119 | # Shared test function to validate expected output. |
91813876 PG |
120 | def compare_show_ip_route_vrf(rname, expected): |
121 | """ | |
6a95bfc8 | 122 | Calls 'show ip ospf vrf [rname]-ospf-cust1 route' for router `rname` and compare the obtained |
91813876 PG |
123 | result with the expected output. |
124 | """ | |
125 | tgen = get_topogen() | |
6a95bfc8 | 126 | vrf_name = "{0}-ospf-cust1".format(rname) |
91813876 | 127 | current = topotest.ip4_route_zebra(tgen.gears[rname], vrf_name) |
787e7624 | 128 | ret = topotest.difflines( |
129 | current, expected, title1="Current output", title2="Expected output" | |
130 | ) | |
91813876 PG |
131 | return ret |
132 | ||
787e7624 | 133 | |
35490676 PG |
134 | def test_ospf_convergence(): |
135 | "Test OSPF daemon convergence" | |
136 | tgen = get_topogen() | |
137 | ||
35490676 | 138 | if tgen.routers_have_failure(): |
787e7624 | 139 | pytest.skip("skipped because of router(s) failure") |
35490676 | 140 | |
e5f0ed14 | 141 | for rname, router in tgen.routers().items(): |
9aecc71d | 142 | logger.info('Waiting for router "%s" convergence', rname) |
35490676 PG |
143 | |
144 | # Load expected results from the command | |
787e7624 | 145 | reffile = os.path.join(CWD, "{}/ospfroute.txt".format(rname)) |
35490676 PG |
146 | expected = open(reffile).read() |
147 | ||
148 | # Run test function until we get an result. Wait at most 60 seconds. | |
787e7624 | 149 | test_func = partial( |
150 | topotest.router_output_cmp, | |
151 | router, | |
6a95bfc8 | 152 | "show ip ospf vrf {0}-ospf-cust1 route".format(rname), |
787e7624 | 153 | expected, |
154 | ) | |
155 | result, diff = topotest.run_and_expect(test_func, "", count=160, wait=0.5) | |
156 | assertmsg = "OSPF did not converge on {}:\n{}".format(rname, diff) | |
9aecc71d RZ |
157 | assert result, assertmsg |
158 | ||
35490676 PG |
159 | |
160 | def test_ospf_kernel_route(): | |
161 | "Test OSPF kernel route installation" | |
162 | tgen = get_topogen() | |
9aecc71d | 163 | |
35490676 | 164 | if tgen.routers_have_failure(): |
787e7624 | 165 | pytest.skip("skipped because of router(s) failure") |
35490676 PG |
166 | |
167 | rlist = tgen.routers().values() | |
168 | for router in rlist: | |
169 | logger.info('Checking OSPF IPv4 kernel routes in "%s"', router.name) | |
787e7624 | 170 | reffile = os.path.join(CWD, "{}/zebraroute.txt".format(router.name)) |
91813876 PG |
171 | expected = open(reffile).read() |
172 | # Run test function until we get an result. Wait at most 60 seconds. | |
173 | test_func = partial(compare_show_ip_route_vrf, router.name, expected) | |
787e7624 | 174 | result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5) |
9aecc71d | 175 | assertmsg = 'OSPF IPv4 route mismatch in router "{}": {}'.format( |
787e7624 | 176 | router.name, diff |
177 | ) | |
9aecc71d RZ |
178 | assert result, assertmsg |
179 | ||
35490676 PG |
180 | |
181 | def test_ospf_json(): | |
182 | "Test 'show ip ospf json' output for coherency." | |
183 | tgen = get_topogen() | |
9aecc71d | 184 | |
35490676 | 185 | if tgen.routers_have_failure(): |
787e7624 | 186 | pytest.skip("skipped because of router(s) failure") |
9aecc71d | 187 | |
e5f0ed14 | 188 | for rname, router in tgen.routers().items(): |
787e7624 | 189 | logger.info( |
6a95bfc8 | 190 | 'Comparing router "%s" "show ip ospf vrf %s-ospf-cust1 json" output', |
787e7624 | 191 | router.name, |
192 | router.name, | |
193 | ) | |
35490676 | 194 | expected = { |
6a95bfc8 CH |
195 | "{}-ospf-cust1".format(router.name): { |
196 | "vrfName": "{}-ospf-cust1".format(router.name), | |
787e7624 | 197 | "routerId": "10.0.255.{}".format(rname[1:]), |
198 | "tosRoutesOnly": True, | |
199 | "rfc2328Conform": True, | |
200 | "spfScheduleDelayMsecs": 0, | |
201 | "holdtimeMinMsecs": 50, | |
202 | "holdtimeMaxMsecs": 5000, | |
203 | "lsaMinIntervalMsecs": 5000, | |
204 | "lsaMinArrivalMsecs": 1000, | |
205 | "writeMultiplier": 20, | |
206 | "refreshTimerMsecs": 10000, | |
207 | "asbrRouter": "injectingExternalRoutingInformation", | |
208 | "attachedAreaCounter": 1, | |
209 | "areas": {}, | |
35490676 | 210 | } |
787e7624 | 211 | } |
35490676 | 212 | # Area specific additional checks |
787e7624 | 213 | if router.name == "r1" or router.name == "r2" or router.name == "r3": |
6a95bfc8 | 214 | expected["{}-ospf-cust1".format(router.name)]["areas"]["0.0.0.0"] = { |
787e7624 | 215 | "areaIfActiveCounter": 2, |
216 | "areaIfTotalCounter": 2, | |
217 | "authentication": "authenticationNone", | |
218 | "backbone": True, | |
219 | "lsaAsbrNumber": 0, | |
220 | "lsaNetworkNumber": 1, | |
221 | "lsaNssaNumber": 0, | |
222 | "lsaNumber": 4, | |
223 | "lsaOpaqueAreaNumber": 0, | |
224 | "lsaOpaqueLinkNumber": 0, | |
225 | "lsaRouterNumber": 3, | |
226 | "lsaSummaryNumber": 0, | |
227 | "nbrFullAdjacentCounter": 2, | |
35490676 PG |
228 | } |
229 | ||
787e7624 | 230 | test_func = partial( |
231 | topotest.router_json_cmp, | |
232 | router, | |
6a95bfc8 | 233 | "show ip ospf vrf {0}-ospf-cust1 json".format(rname), |
787e7624 | 234 | expected, |
235 | ) | |
236 | _, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) | |
9aecc71d RZ |
237 | assertmsg = '"{}" JSON output mismatches'.format(rname) |
238 | assert diff is None, assertmsg | |
239 | ||
35490676 PG |
240 | |
241 | def test_ospf_link_down(): | |
242 | "Test OSPF convergence after a link goes down" | |
243 | tgen = get_topogen() | |
9aecc71d | 244 | |
35490676 | 245 | if tgen.routers_have_failure(): |
787e7624 | 246 | pytest.skip("skipped because of router(s) failure") |
35490676 PG |
247 | |
248 | # Simulate a network down event on router3 switch3 interface. | |
787e7624 | 249 | router3 = tgen.gears["r3"] |
250 | topotest.interface_set_status( | |
6a95bfc8 | 251 | router3, "r3-eth0", ifaceaction=False, vrf_name="r3-ospf-cust1" |
787e7624 | 252 | ) |
35490676 PG |
253 | |
254 | # Expect convergence on all routers | |
e5f0ed14 | 255 | for rname, router in tgen.routers().items(): |
9aecc71d | 256 | logger.info('Waiting for router "%s" convergence after link failure', rname) |
35490676 | 257 | # Load expected results from the command |
787e7624 | 258 | reffile = os.path.join(CWD, "{}/ospfroute_down.txt".format(rname)) |
35490676 PG |
259 | expected = open(reffile).read() |
260 | ||
261 | # Run test function until we get an result. Wait at most 60 seconds. | |
787e7624 | 262 | test_func = partial( |
263 | topotest.router_output_cmp, | |
264 | router, | |
6a95bfc8 | 265 | "show ip ospf vrf {0}-ospf-cust1 route".format(rname), |
787e7624 | 266 | expected, |
267 | ) | |
268 | result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5) | |
269 | assertmsg = "OSPF did not converge on {}:\n{}".format(rname, diff) | |
9aecc71d RZ |
270 | assert result, assertmsg |
271 | ||
35490676 PG |
272 | |
273 | def test_ospf_link_down_kernel_route(): | |
274 | "Test OSPF kernel route installation" | |
275 | tgen = get_topogen() | |
9aecc71d | 276 | |
35490676 | 277 | if tgen.routers_have_failure(): |
787e7624 | 278 | pytest.skip("skipped because of router(s) failure") |
35490676 PG |
279 | |
280 | rlist = tgen.routers().values() | |
281 | for router in rlist: | |
787e7624 | 282 | logger.info( |
283 | 'Checking OSPF IPv4 kernel routes in "%s" after link down', router.name | |
284 | ) | |
35490676 | 285 | |
6a95bfc8 | 286 | str = "{0}-ospf-cust1".format(router.name) |
787e7624 | 287 | reffile = os.path.join(CWD, "{}/zebraroutedown.txt".format(router.name)) |
91813876 PG |
288 | expected = open(reffile).read() |
289 | # Run test function until we get an result. Wait at most 60 seconds. | |
290 | test_func = partial(compare_show_ip_route_vrf, router.name, expected) | |
787e7624 | 291 | result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5) |
9fa6ec14 | 292 | assertmsg = ( |
293 | 'OSPF IPv4 route mismatch in router "{}" after link down: {}'.format( | |
294 | router.name, diff | |
295 | ) | |
787e7624 | 296 | ) |
9aecc71d RZ |
297 | assert result, assertmsg |
298 | ||
35490676 PG |
299 | |
300 | def test_memory_leak(): | |
301 | "Run the memory leak test and report results." | |
302 | tgen = get_topogen() | |
303 | if not tgen.is_memleak_enabled(): | |
787e7624 | 304 | pytest.skip("Memory leak test/report is disabled") |
35490676 PG |
305 | |
306 | tgen.report_memory_leaks() | |
307 | ||
787e7624 | 308 | |
309 | if __name__ == "__main__": | |
35490676 PG |
310 | args = ["-s"] + sys.argv[1:] |
311 | sys.exit(pytest.main(args)) |