]>
Commit | Line | Data |
---|---|---|
4256a209 | 1 | #!/usr/bin/python |
2 | ||
3 | # | |
4 | # Copyright (c) 2020 by VMware, Inc. ("VMware") | |
5 | # Used Copyright (c) 2018 by Network Device Education Foundation, Inc. | |
6 | # ("NetDEF") in this file. | |
7 | # | |
8 | # Permission to use, copy, modify, and/or distribute this software | |
9 | # for any purpose with or without fee is hereby granted, provided | |
10 | # that the above copyright notice and this permission notice appear | |
11 | # in all copies. | |
12 | # | |
13 | # THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES | |
14 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
15 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR | |
16 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY | |
17 | # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
18 | # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS | |
19 | # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |
20 | # OF THIS SOFTWARE. | |
21 | # | |
22 | ||
23 | ||
24 | """OSPF Basic Functionality Automation.""" | |
25 | import os | |
26 | import sys | |
27 | import time | |
28 | import pytest | |
29 | import json | |
30 | from time import sleep | |
31 | from copy import deepcopy | |
32 | ||
33 | # Save the Current Working Directory to find configuration files. | |
34 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
35 | sys.path.append(os.path.join(CWD, "../")) | |
36 | sys.path.append(os.path.join(CWD, "../lib/")) | |
37 | ||
38 | # pylint: disable=C0413 | |
39 | # Import topogen and topotest helpers | |
40 | from mininet.topo import Topo | |
41 | from lib.topogen import Topogen, get_topogen | |
42 | from ipaddress import IPv4Address | |
43 | ||
44 | # Import topoJson from lib, to create topology and initial configuration | |
45 | from lib.common_config import ( | |
46 | start_topology, | |
47 | write_test_header, | |
48 | write_test_footer, | |
49 | reset_config_on_routers, | |
50 | verify_rib, | |
51 | create_static_routes, | |
52 | step, | |
53 | create_route_maps, | |
54 | shutdown_bringup_interface, | |
55 | create_interfaces_cfg, | |
701a0192 | 56 | topo_daemons, |
4256a209 | 57 | ) |
58 | from lib.topolog import logger | |
59 | ||
60 | from lib.topojson import build_topo_from_json, build_config_from_json | |
61 | from lib.ospf import ( | |
62 | verify_ospf_neighbor, | |
63 | config_ospf_interface, | |
64 | clear_ospf, | |
65 | verify_ospf_rib, | |
66 | create_router_ospf, | |
67 | verify_ospf_interface, | |
68 | ) | |
69 | ||
70 | topo = None | |
71 | ||
72 | # Reading the data from JSON File for topology creation | |
73 | jsonFile = "{}/ospf_ecmp.json".format(CWD) | |
74 | ||
75 | try: | |
76 | with open(jsonFile, "r") as topoJson: | |
77 | topo = json.load(topoJson) | |
78 | except IOError: | |
79 | assert False, "Could not read file {}".format(jsonFile) | |
80 | ||
81 | # Global variables | |
82 | NETWORK = { | |
83 | "ipv4": [ | |
84 | "11.0.20.1/32", | |
85 | "11.0.20.2/32", | |
86 | "11.0.20.3/32", | |
87 | "11.0.20.4/32", | |
88 | "11.0.20.5/32", | |
89 | ] | |
90 | } | |
91 | """ | |
92 | TOPOLOGY : | |
93 | Please view in a fixed-width font such as Courier. | |
94 | +---+ A1 +---+ | |
95 | +R1 +------------+R2 | | |
96 | +-+-+- +--++ | |
97 | | -- -- | | |
98 | | -- A0 -- | | |
99 | A0| ---- | | |
100 | | ---- | A2 | |
101 | | -- -- | | |
102 | | -- -- | | |
103 | +-+-+- +-+-+ | |
104 | +R0 +-------------+R3 | | |
105 | +---+ A3 +---+ | |
106 | ||
107 | TESTCASES : | |
108 | 1. Verify OSPF ECMP with max path configured as 8 (ECMPconfigured at FRR level) | |
109 | 2. Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports) | |
110 | """ | |
111 | ||
112 | ||
113 | class CreateTopo(Topo): | |
114 | """ | |
115 | Test topology builder. | |
116 | ||
117 | * `Topo`: Topology object | |
118 | """ | |
119 | ||
120 | def build(self, *_args, **_opts): | |
121 | """Build function.""" | |
122 | tgen = get_topogen(self) | |
123 | ||
124 | # Building topology from json file | |
125 | build_topo_from_json(tgen, topo) | |
126 | ||
127 | ||
128 | def setup_module(mod): | |
129 | """ | |
130 | Sets up the pytest environment | |
131 | ||
132 | * `mod`: module name | |
133 | """ | |
134 | global topo | |
135 | testsuite_run_time = time.asctime(time.localtime(time.time())) | |
136 | logger.info("Testsuite start time: {}".format(testsuite_run_time)) | |
137 | logger.info("=" * 40) | |
138 | ||
139 | logger.info("Running setup_module to create topology") | |
140 | ||
141 | # This function initiates the topology build with Topogen... | |
142 | tgen = Topogen(CreateTopo, mod.__name__) | |
143 | # ... and here it calls Mininet initialization functions. | |
144 | ||
035267a3 | 145 | # get list of daemons needs to be started for this suite. |
146 | daemons = topo_daemons(tgen, topo) | |
147 | ||
4256a209 | 148 | # Starting topology, create tmp files which are loaded to routers |
149 | # to start deamons and then start routers | |
035267a3 | 150 | start_topology(tgen, daemons) |
4256a209 | 151 | |
152 | # Creating configuration from JSON | |
153 | build_config_from_json(tgen, topo) | |
154 | ||
155 | # Don't run this test if we have any failure. | |
156 | if tgen.routers_have_failure(): | |
157 | pytest.skip(tgen.errors) | |
158 | # Api call verify whether OSPF is converged | |
159 | ospf_covergence = verify_ospf_neighbor(tgen, topo) | |
160 | assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( | |
161 | ospf_covergence | |
162 | ) | |
163 | ||
164 | logger.info("Running setup_module() done") | |
165 | ||
166 | ||
167 | def teardown_module(mod): | |
168 | """ | |
169 | Teardown the pytest environment. | |
170 | ||
171 | * `mod`: module name | |
172 | """ | |
173 | ||
174 | logger.info("Running teardown_module to delete topology") | |
175 | ||
176 | tgen = get_topogen() | |
177 | ||
178 | # Stop toplogy and Remove tmp files | |
179 | tgen.stop_topology() | |
180 | ||
181 | logger.info( | |
182 | "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) | |
183 | ) | |
184 | logger.info("=" * 40) | |
185 | ||
186 | ||
881fa68f | 187 | def redistribute(dut, route_type, **kwargs): |
188 | """Local def for redstribution of routes inside ospf.""" | |
189 | global topo | |
190 | tgen = get_topogen() | |
191 | ||
192 | ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": route_type}]}}} | |
193 | for k, v in kwargs.items(): | |
194 | ospf_red[dut]["ospf"]["redistribute"][0][k] = v | |
195 | ||
196 | result = create_router_ospf(tgen, topo, ospf_red) | |
197 | assert result is True, "Testcase : Failed \n Error: {}".format(result) | |
4256a209 | 198 | |
199 | ||
200 | # ################################## | |
201 | # Test cases start here. | |
202 | # ################################## | |
203 | ||
204 | ||
205 | def test_ospf_ecmp_tc16_p0(request): | |
206 | """ | |
207 | Verify OSPF ECMP. | |
208 | ||
209 | Verify OSPF ECMP with max path configured as 8 (ECMP | |
210 | configured at FRR level) | |
211 | """ | |
212 | tc_name = request.node.name | |
213 | write_test_header(tc_name) | |
214 | tgen = get_topogen() | |
215 | ||
216 | # Don't run this test if we have any failure. | |
217 | if tgen.routers_have_failure(): | |
218 | pytest.skip(tgen.errors) | |
219 | ||
220 | global topo | |
221 | step("Bring up the base config as per the topology") | |
222 | step("Configure 8 interfaces between R1 and R2 and enable ospf in area 0.") | |
223 | reset_config_on_routers(tgen) | |
224 | step("Verify that OSPF is up with 8 neighborship sessions.") | |
225 | dut = "r1" | |
226 | ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) | |
227 | assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( | |
228 | ospf_covergence | |
229 | ) | |
230 | ||
231 | step("Configure a static route in R0 and redistribute in OSPF.") | |
232 | ||
233 | input_dict = { | |
234 | "r0": { | |
235 | "static_routes": [ | |
9fa6ec14 | 236 | { |
237 | "network": NETWORK["ipv4"][0], | |
238 | "no_of_ip": 5, | |
239 | "next_hop": "Null0", | |
240 | } | |
4256a209 | 241 | ] |
242 | } | |
243 | } | |
244 | result = create_static_routes(tgen, input_dict) | |
245 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
246 | ||
247 | dut = "r0" | |
c6b59ef4 | 248 | redistribute(dut, "static") |
4256a209 | 249 | |
250 | step("Verify that route in R2 in stalled with 8 next hops.") | |
251 | nh = [] | |
252 | for item in range(1, 7): | |
253 | nh.append(topo["routers"]["r0"]["links"]["r1-link1"]["ipv4"].split("/")[0]) | |
254 | ||
255 | nh2 = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] | |
256 | ||
257 | nh.append(nh2) | |
258 | ||
259 | dut = "r1" | |
260 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
261 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
262 | ||
263 | protocol = "ospf" | |
264 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
265 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
266 | ||
267 | step("shut no shut all the interfaces on the remote router - R2") | |
268 | dut = "r1" | |
269 | for intfr in range(1, 7): | |
270 | intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] | |
271 | shutdown_bringup_interface(tgen, dut, intf, False) | |
272 | ||
273 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False) | |
274 | assert result is not True, "Testcase {} : Failed \n Error: {}".format( | |
275 | tc_name, result | |
276 | ) | |
277 | ||
278 | protocol = "ospf" | |
279 | result = verify_rib( | |
280 | tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh, expected=False | |
281 | ) | |
282 | assert result is not True, "Testcase {} : Failed \n Error: {}".format( | |
283 | tc_name, result | |
284 | ) | |
285 | ||
286 | for intfr in range(1, 7): | |
287 | intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] | |
288 | shutdown_bringup_interface(tgen, dut, intf, True) | |
289 | ||
290 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
291 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
292 | ||
293 | protocol = "ospf" | |
294 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
295 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
296 | ||
297 | step("shut no shut on all the interfaces on DUT (r1)") | |
298 | for intfr in range(1, 7): | |
299 | intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] | |
300 | shutdown_bringup_interface(tgen, dut, intf, False) | |
301 | ||
302 | for intfr in range(1, 7): | |
303 | intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] | |
304 | shutdown_bringup_interface(tgen, dut, intf, True) | |
305 | ||
306 | step( | |
307 | "Verify that all the neighbours are up and routes are installed" | |
308 | " with 8 next hop in ospf and ip route tables on R1." | |
309 | ) | |
310 | ||
311 | step("Verify that OSPF is up with 8 neighborship sessions.") | |
312 | dut = "r1" | |
313 | ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) | |
314 | assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( | |
315 | ospf_covergence | |
316 | ) | |
317 | ||
318 | dut = "r1" | |
319 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
320 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
321 | ||
322 | protocol = "ospf" | |
323 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
324 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
325 | ||
326 | step(" Un configure static route on R0") | |
327 | ||
328 | dut = "r0" | |
c6b59ef4 | 329 | redistribute(dut, "static", delete=True) |
4256a209 | 330 | |
331 | # Wait for R0 to flush external LSAs. | |
332 | sleep(10) | |
333 | ||
334 | step("Verify that route is withdrawn from R2.") | |
335 | dut = "r1" | |
336 | result = verify_ospf_rib( | |
337 | tgen, dut, input_dict, next_hop=nh, attempts=5, expected=False | |
338 | ) | |
339 | assert result is not True, "Testcase {} : Failed \n Error: {}".format( | |
340 | tc_name, result | |
341 | ) | |
342 | ||
343 | protocol = "ospf" | |
a81774ec | 344 | result = verify_rib( |
345 | tgen, | |
346 | "ipv4", | |
347 | dut, | |
348 | input_dict, | |
349 | protocol=protocol, | |
350 | next_hop=nh, | |
351 | attempts=5, | |
352 | expected=False, | |
353 | ) | |
4256a209 | 354 | assert result is not True, "Testcase {} : Failed \n Error: {}".format( |
355 | tc_name, result | |
356 | ) | |
357 | ||
358 | step("Re configure the static route in R0.") | |
359 | dut = "r0" | |
c6b59ef4 | 360 | redistribute(dut, "static") |
4256a209 | 361 | |
362 | dut = "r1" | |
363 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
364 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
365 | ||
366 | protocol = "ospf" | |
367 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
368 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
369 | ||
370 | write_test_footer(tc_name) | |
371 | ||
372 | ||
373 | def test_ospf_ecmp_tc17_p0(request): | |
374 | """ | |
375 | Verify OSPF ECMP. | |
376 | ||
377 | Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports) | |
378 | """ | |
379 | tc_name = request.node.name | |
380 | write_test_header(tc_name) | |
381 | tgen = get_topogen() | |
382 | ||
383 | # Don't run this test if we have any failure. | |
384 | if tgen.routers_have_failure(): | |
385 | pytest.skip(tgen.errors) | |
386 | ||
387 | global topo | |
388 | step("Bring up the base config as per the topology") | |
389 | step("Configure 2 interfaces between R1 and R2 & enable ospf in area 0.") | |
390 | reset_config_on_routers(tgen) | |
391 | step("Verify that OSPF is up with 2 neighborship sessions.") | |
392 | dut = "r1" | |
393 | ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) | |
394 | assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( | |
395 | ospf_covergence | |
396 | ) | |
397 | ||
398 | step("Configure a static route in R0 and redistribute in OSPF.") | |
399 | ||
400 | input_dict = { | |
401 | "r0": { | |
402 | "static_routes": [ | |
9fa6ec14 | 403 | { |
404 | "network": NETWORK["ipv4"][0], | |
405 | "no_of_ip": 5, | |
406 | "next_hop": "Null0", | |
407 | } | |
4256a209 | 408 | ] |
409 | } | |
410 | } | |
411 | result = create_static_routes(tgen, input_dict) | |
412 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
413 | ||
414 | dut = "r0" | |
c6b59ef4 | 415 | redistribute(dut, "static") |
4256a209 | 416 | |
417 | step("Verify that route in R2 in stalled with 2 next hops.") | |
418 | ||
419 | nh1 = topo["routers"]["r0"]["links"]["r1-link1"]["ipv4"].split("/")[0] | |
420 | nh2 = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] | |
421 | nh = [nh1, nh2] | |
422 | ||
423 | dut = "r1" | |
424 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
425 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
426 | ||
427 | protocol = "ospf" | |
428 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
429 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
430 | ||
431 | step(" Un configure static route on R0") | |
432 | ||
433 | dut = "r0" | |
c6b59ef4 | 434 | redistribute(dut, "static", delete=True) |
4256a209 | 435 | # sleep till the route gets withdrawn |
436 | sleep(10) | |
437 | ||
438 | step("Verify that route is withdrawn from R2.") | |
439 | dut = "r1" | |
a81774ec | 440 | result = verify_ospf_rib( |
441 | tgen, dut, input_dict, next_hop=nh, attempts=5, expected=False | |
442 | ) | |
4256a209 | 443 | assert result is not True, "Testcase {} : Failed \n Error: {}".format( |
444 | tc_name, result | |
445 | ) | |
446 | ||
447 | protocol = "ospf" | |
a81774ec | 448 | result = verify_rib( |
449 | tgen, | |
450 | "ipv4", | |
451 | dut, | |
452 | input_dict, | |
453 | protocol=protocol, | |
454 | next_hop=nh, | |
455 | attempts=5, | |
456 | expected=False, | |
457 | ) | |
4256a209 | 458 | assert result is not True, "Testcase {} : Failed \n Error: {}".format( |
459 | tc_name, result | |
460 | ) | |
461 | ||
462 | step("Reconfigure the static route in R0.Change ECMP value to 2.") | |
463 | dut = "r0" | |
c6b59ef4 | 464 | redistribute(dut, "static") |
4256a209 | 465 | |
466 | step("Configure cost on R0 as 100") | |
467 | r0_ospf_cost = {"r0": {"links": {"r1": {"ospf": {"cost": 100}}}}} | |
468 | result = config_ospf_interface(tgen, topo, r0_ospf_cost) | |
469 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
470 | ||
471 | dut = "r1" | |
472 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
473 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
474 | ||
475 | protocol = "ospf" | |
476 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
477 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
478 | ||
479 | write_test_footer(tc_name) | |
480 | ||
481 | ||
482 | if __name__ == "__main__": | |
483 | args = ["-s"] + sys.argv[1:] | |
484 | sys.exit(pytest.main(args)) |