]>
Commit | Line | Data |
---|---|---|
4256a209 | 1 | #!/usr/bin/python |
acddc0ed | 2 | # SPDX-License-Identifier: ISC |
4256a209 | 3 | |
4 | # | |
5 | # Copyright (c) 2020 by VMware, Inc. ("VMware") | |
6 | # Used Copyright (c) 2018 by Network Device Education Foundation, Inc. | |
7 | # ("NetDEF") in this file. | |
8 | # | |
4256a209 | 9 | |
10 | ||
11 | """OSPF Basic Functionality Automation.""" | |
12 | import os | |
13 | import sys | |
14 | import time | |
15 | import pytest | |
4256a209 | 16 | from time import sleep |
4256a209 | 17 | |
18 | # Save the Current Working Directory to find configuration files. | |
19 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
20 | sys.path.append(os.path.join(CWD, "../")) | |
21 | sys.path.append(os.path.join(CWD, "../lib/")) | |
22 | ||
23 | # pylint: disable=C0413 | |
24 | # Import topogen and topotest helpers | |
4256a209 | 25 | from lib.topogen import Topogen, get_topogen |
4256a209 | 26 | |
27 | # Import topoJson from lib, to create topology and initial configuration | |
28 | from lib.common_config import ( | |
29 | start_topology, | |
30 | write_test_header, | |
31 | write_test_footer, | |
32 | reset_config_on_routers, | |
33 | verify_rib, | |
34 | create_static_routes, | |
35 | step, | |
4256a209 | 36 | shutdown_bringup_interface, |
4256a209 | 37 | ) |
38 | from lib.topolog import logger | |
39 | ||
4953ca97 | 40 | from lib.topojson import build_config_from_json |
4256a209 | 41 | from lib.ospf import ( |
42 | verify_ospf_neighbor, | |
43 | config_ospf_interface, | |
4256a209 | 44 | verify_ospf_rib, |
88b7d3e7 | 45 | redistribute_ospf, |
4256a209 | 46 | ) |
47 | ||
6ff492b1 DS |
48 | pytestmark = [pytest.mark.ospfd, pytest.mark.staticd] |
49 | ||
50 | ||
4256a209 | 51 | topo = None |
52 | ||
4256a209 | 53 | |
54 | # Global variables | |
55 | NETWORK = { | |
56 | "ipv4": [ | |
57 | "11.0.20.1/32", | |
58 | "11.0.20.2/32", | |
59 | "11.0.20.3/32", | |
60 | "11.0.20.4/32", | |
61 | "11.0.20.5/32", | |
62 | ] | |
63 | } | |
64 | """ | |
65 | TOPOLOGY : | |
66 | Please view in a fixed-width font such as Courier. | |
67 | +---+ A1 +---+ | |
68 | +R1 +------------+R2 | | |
69 | +-+-+- +--++ | |
70 | | -- -- | | |
71 | | -- A0 -- | | |
72 | A0| ---- | | |
73 | | ---- | A2 | |
74 | | -- -- | | |
75 | | -- -- | | |
76 | +-+-+- +-+-+ | |
77 | +R0 +-------------+R3 | | |
78 | +---+ A3 +---+ | |
79 | ||
80 | TESTCASES : | |
81 | 1. Verify OSPF ECMP with max path configured as 8 (ECMPconfigured at FRR level) | |
82 | 2. Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports) | |
83 | """ | |
84 | ||
85 | ||
4256a209 | 86 | def setup_module(mod): |
87 | """ | |
88 | Sets up the pytest environment | |
89 | ||
90 | * `mod`: module name | |
91 | """ | |
4256a209 | 92 | testsuite_run_time = time.asctime(time.localtime(time.time())) |
93 | logger.info("Testsuite start time: {}".format(testsuite_run_time)) | |
94 | logger.info("=" * 40) | |
95 | ||
96 | logger.info("Running setup_module to create topology") | |
97 | ||
98 | # This function initiates the topology build with Topogen... | |
e82b531d CH |
99 | json_file = "{}/ospf_ecmp.json".format(CWD) |
100 | tgen = Topogen(json_file, mod.__name__) | |
101 | global topo | |
102 | topo = tgen.json_topo | |
4256a209 | 103 | # ... and here it calls Mininet initialization functions. |
104 | ||
105 | # Starting topology, create tmp files which are loaded to routers | |
d60a3f0e | 106 | # to start daemons and then start routers |
991a971f | 107 | start_topology(tgen) |
4256a209 | 108 | |
109 | # Creating configuration from JSON | |
110 | build_config_from_json(tgen, topo) | |
111 | ||
112 | # Don't run this test if we have any failure. | |
113 | if tgen.routers_have_failure(): | |
114 | pytest.skip(tgen.errors) | |
115 | # Api call verify whether OSPF is converged | |
116 | ospf_covergence = verify_ospf_neighbor(tgen, topo) | |
74dd0c84 | 117 | assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( |
4256a209 | 118 | ospf_covergence |
119 | ) | |
120 | ||
121 | logger.info("Running setup_module() done") | |
122 | ||
123 | ||
124 | def teardown_module(mod): | |
125 | """ | |
126 | Teardown the pytest environment. | |
127 | ||
128 | * `mod`: module name | |
129 | """ | |
130 | ||
131 | logger.info("Running teardown_module to delete topology") | |
132 | ||
133 | tgen = get_topogen() | |
134 | ||
135 | # Stop toplogy and Remove tmp files | |
136 | tgen.stop_topology() | |
137 | ||
138 | logger.info( | |
139 | "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) | |
140 | ) | |
141 | logger.info("=" * 40) | |
142 | ||
143 | ||
4256a209 | 144 | # ################################## |
145 | # Test cases start here. | |
146 | # ################################## | |
147 | ||
148 | ||
149 | def test_ospf_ecmp_tc16_p0(request): | |
150 | """ | |
151 | Verify OSPF ECMP. | |
152 | ||
153 | Verify OSPF ECMP with max path configured as 8 (ECMP | |
154 | configured at FRR level) | |
155 | """ | |
156 | tc_name = request.node.name | |
157 | write_test_header(tc_name) | |
158 | tgen = get_topogen() | |
159 | ||
160 | # Don't run this test if we have any failure. | |
161 | if tgen.routers_have_failure(): | |
162 | pytest.skip(tgen.errors) | |
163 | ||
164 | global topo | |
165 | step("Bring up the base config as per the topology") | |
166 | step("Configure 8 interfaces between R1 and R2 and enable ospf in area 0.") | |
167 | reset_config_on_routers(tgen) | |
168 | step("Verify that OSPF is up with 8 neighborship sessions.") | |
169 | dut = "r1" | |
170 | ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) | |
74dd0c84 | 171 | assert ospf_covergence is True, "Testcase Failed \n Error {}".format( |
4256a209 | 172 | ospf_covergence |
173 | ) | |
174 | ||
175 | step("Configure a static route in R0 and redistribute in OSPF.") | |
176 | ||
177 | input_dict = { | |
178 | "r0": { | |
179 | "static_routes": [ | |
9fa6ec14 | 180 | { |
181 | "network": NETWORK["ipv4"][0], | |
182 | "no_of_ip": 5, | |
183 | "next_hop": "Null0", | |
184 | } | |
4256a209 | 185 | ] |
186 | } | |
187 | } | |
188 | result = create_static_routes(tgen, input_dict) | |
189 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
190 | ||
191 | dut = "r0" | |
88b7d3e7 | 192 | redistribute_ospf(tgen, topo, dut, "static") |
4256a209 | 193 | |
194 | step("Verify that route in R2 in stalled with 8 next hops.") | |
195 | nh = [] | |
196 | for item in range(1, 7): | |
197 | nh.append(topo["routers"]["r0"]["links"]["r1-link1"]["ipv4"].split("/")[0]) | |
198 | ||
199 | nh2 = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] | |
200 | ||
201 | nh.append(nh2) | |
202 | ||
203 | dut = "r1" | |
204 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
205 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
206 | ||
207 | protocol = "ospf" | |
208 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
209 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
210 | ||
211 | step("shut no shut all the interfaces on the remote router - R2") | |
212 | dut = "r1" | |
213 | for intfr in range(1, 7): | |
214 | intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] | |
215 | shutdown_bringup_interface(tgen, dut, intf, False) | |
216 | ||
217 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False) | |
0b25370e DS |
218 | assert ( |
219 | result is not True | |
74dd0c84 | 220 | ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( |
4256a209 | 221 | tc_name, result |
0b25370e | 222 | ) |
4256a209 | 223 | |
224 | protocol = "ospf" | |
225 | result = verify_rib( | |
226 | tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh, expected=False | |
227 | ) | |
0b25370e DS |
228 | assert ( |
229 | result is not True | |
74dd0c84 | 230 | ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( |
4256a209 | 231 | tc_name, result |
0b25370e | 232 | ) |
4256a209 | 233 | |
234 | for intfr in range(1, 7): | |
235 | intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] | |
236 | shutdown_bringup_interface(tgen, dut, intf, True) | |
237 | ||
238 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
239 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
240 | ||
241 | protocol = "ospf" | |
242 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
243 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
244 | ||
245 | step("shut no shut on all the interfaces on DUT (r1)") | |
246 | for intfr in range(1, 7): | |
247 | intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] | |
248 | shutdown_bringup_interface(tgen, dut, intf, False) | |
249 | ||
250 | for intfr in range(1, 7): | |
251 | intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] | |
252 | shutdown_bringup_interface(tgen, dut, intf, True) | |
253 | ||
254 | step( | |
255 | "Verify that all the neighbours are up and routes are installed" | |
256 | " with 8 next hop in ospf and ip route tables on R1." | |
257 | ) | |
258 | ||
259 | step("Verify that OSPF is up with 8 neighborship sessions.") | |
260 | dut = "r1" | |
261 | ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) | |
74dd0c84 | 262 | assert ospf_covergence is True, "Testcase Failed \n Error {}".format( |
4256a209 | 263 | ospf_covergence |
264 | ) | |
265 | ||
266 | dut = "r1" | |
267 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
268 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
269 | ||
270 | protocol = "ospf" | |
271 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
272 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
273 | ||
274 | step(" Un configure static route on R0") | |
275 | ||
276 | dut = "r0" | |
88b7d3e7 | 277 | redistribute_ospf(tgen, topo, dut, "static", delete=True) |
4256a209 | 278 | |
279 | # Wait for R0 to flush external LSAs. | |
280 | sleep(10) | |
281 | ||
282 | step("Verify that route is withdrawn from R2.") | |
283 | dut = "r1" | |
284 | result = verify_ospf_rib( | |
ed776e38 | 285 | tgen, dut, input_dict, next_hop=nh, retry_timeout=10, expected=False |
4256a209 | 286 | ) |
0b25370e DS |
287 | assert ( |
288 | result is not True | |
74dd0c84 | 289 | ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( |
4256a209 | 290 | tc_name, result |
0b25370e | 291 | ) |
4256a209 | 292 | |
293 | protocol = "ospf" | |
a81774ec | 294 | result = verify_rib( |
295 | tgen, | |
296 | "ipv4", | |
297 | dut, | |
298 | input_dict, | |
299 | protocol=protocol, | |
300 | next_hop=nh, | |
ed776e38 | 301 | retry_timeout=10, |
a81774ec | 302 | expected=False, |
303 | ) | |
0b25370e DS |
304 | assert ( |
305 | result is not True | |
74dd0c84 | 306 | ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( |
4256a209 | 307 | tc_name, result |
0b25370e | 308 | ) |
4256a209 | 309 | |
310 | step("Re configure the static route in R0.") | |
311 | dut = "r0" | |
88b7d3e7 | 312 | redistribute_ospf(tgen, topo, dut, "static") |
4256a209 | 313 | |
314 | dut = "r1" | |
315 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
316 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
317 | ||
318 | protocol = "ospf" | |
319 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
320 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
321 | ||
322 | write_test_footer(tc_name) | |
323 | ||
324 | ||
325 | def test_ospf_ecmp_tc17_p0(request): | |
326 | """ | |
327 | Verify OSPF ECMP. | |
328 | ||
329 | Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports) | |
330 | """ | |
331 | tc_name = request.node.name | |
332 | write_test_header(tc_name) | |
333 | tgen = get_topogen() | |
334 | ||
335 | # Don't run this test if we have any failure. | |
336 | if tgen.routers_have_failure(): | |
337 | pytest.skip(tgen.errors) | |
338 | ||
339 | global topo | |
340 | step("Bring up the base config as per the topology") | |
341 | step("Configure 2 interfaces between R1 and R2 & enable ospf in area 0.") | |
342 | reset_config_on_routers(tgen) | |
343 | step("Verify that OSPF is up with 2 neighborship sessions.") | |
344 | dut = "r1" | |
345 | ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) | |
74dd0c84 | 346 | assert ospf_covergence is True, "Testcase Failed \n Error {}".format( |
4256a209 | 347 | ospf_covergence |
348 | ) | |
349 | ||
350 | step("Configure a static route in R0 and redistribute in OSPF.") | |
351 | ||
352 | input_dict = { | |
353 | "r0": { | |
354 | "static_routes": [ | |
9fa6ec14 | 355 | { |
356 | "network": NETWORK["ipv4"][0], | |
357 | "no_of_ip": 5, | |
358 | "next_hop": "Null0", | |
359 | } | |
4256a209 | 360 | ] |
361 | } | |
362 | } | |
363 | result = create_static_routes(tgen, input_dict) | |
364 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
365 | ||
366 | dut = "r0" | |
88b7d3e7 | 367 | redistribute_ospf(tgen, topo, dut, "static") |
4256a209 | 368 | |
369 | step("Verify that route in R2 in stalled with 2 next hops.") | |
370 | ||
371 | nh1 = topo["routers"]["r0"]["links"]["r1-link1"]["ipv4"].split("/")[0] | |
372 | nh2 = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] | |
373 | nh = [nh1, nh2] | |
374 | ||
375 | dut = "r1" | |
376 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
377 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
378 | ||
379 | protocol = "ospf" | |
380 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
381 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
382 | ||
383 | step(" Un configure static route on R0") | |
384 | ||
385 | dut = "r0" | |
88b7d3e7 | 386 | redistribute_ospf(tgen, topo, dut, "static", delete=True) |
4256a209 | 387 | # sleep till the route gets withdrawn |
388 | sleep(10) | |
389 | ||
390 | step("Verify that route is withdrawn from R2.") | |
391 | dut = "r1" | |
a81774ec | 392 | result = verify_ospf_rib( |
ed776e38 | 393 | tgen, dut, input_dict, next_hop=nh, retry_timeout=10, expected=False |
a81774ec | 394 | ) |
0b25370e DS |
395 | assert ( |
396 | result is not True | |
74dd0c84 | 397 | ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( |
4256a209 | 398 | tc_name, result |
0b25370e | 399 | ) |
4256a209 | 400 | |
401 | protocol = "ospf" | |
a81774ec | 402 | result = verify_rib( |
403 | tgen, | |
404 | "ipv4", | |
405 | dut, | |
406 | input_dict, | |
407 | protocol=protocol, | |
408 | next_hop=nh, | |
ed776e38 | 409 | retry_timeout=10, |
a81774ec | 410 | expected=False, |
411 | ) | |
0b25370e DS |
412 | assert ( |
413 | result is not True | |
74dd0c84 | 414 | ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( |
4256a209 | 415 | tc_name, result |
0b25370e | 416 | ) |
4256a209 | 417 | |
418 | step("Reconfigure the static route in R0.Change ECMP value to 2.") | |
419 | dut = "r0" | |
88b7d3e7 | 420 | redistribute_ospf(tgen, topo, dut, "static") |
4256a209 | 421 | |
422 | step("Configure cost on R0 as 100") | |
423 | r0_ospf_cost = {"r0": {"links": {"r1": {"ospf": {"cost": 100}}}}} | |
424 | result = config_ospf_interface(tgen, topo, r0_ospf_cost) | |
425 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
426 | ||
427 | dut = "r1" | |
428 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
429 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
430 | ||
431 | protocol = "ospf" | |
432 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
433 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
434 | ||
435 | write_test_footer(tc_name) | |
436 | ||
437 | ||
438 | if __name__ == "__main__": | |
439 | args = ["-s"] + sys.argv[1:] | |
440 | sys.exit(pytest.main(args)) |