]>
Commit | Line | Data |
---|---|---|
dc5298d7 | 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 | |
dc5298d7 | 29 | from copy import deepcopy |
30 | from ipaddress import IPv4Address | |
31 | ||
32 | # Save the Current Working Directory to find configuration files. | |
33 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
34 | sys.path.append(os.path.join(CWD, "../")) | |
35 | sys.path.append(os.path.join(CWD, "../lib/")) | |
36 | ||
37 | # pylint: disable=C0413 | |
38 | # Import topogen and topotest helpers | |
dc5298d7 | 39 | from lib.topogen import Topogen, get_topogen |
dc5298d7 | 40 | |
41 | # Import topoJson from lib, to create topology and initial configuration | |
42 | from lib.common_config import ( | |
43 | start_topology, | |
44 | write_test_header, | |
45 | write_test_footer, | |
46 | reset_config_on_routers, | |
dc5298d7 | 47 | step, |
dc5298d7 | 48 | create_interfaces_cfg, |
49 | topo_daemons, | |
675ba67d LB |
50 | retry, |
51 | run_frr_cmd, | |
dc5298d7 | 52 | ) |
53 | from lib.topolog import logger | |
4953ca97 | 54 | from lib.topojson import build_config_from_json |
675ba67d | 55 | from lib.topotest import frr_unicode, json_cmp |
dc5298d7 | 56 | |
57 | from lib.ospf import ( | |
dc5298d7 | 58 | verify_ospf_interface, |
dc5298d7 | 59 | ) |
60 | ||
6ff492b1 | 61 | |
e82b531d | 62 | pytestmark = [pytest.mark.ospfd, pytest.mark.staticd] |
6ff492b1 | 63 | |
dc5298d7 | 64 | # Global variables |
65 | topo = None | |
66 | ||
dc5298d7 | 67 | |
68 | """ | |
69 | TOPOOLOGY = | |
70 | Please view in a fixed-width font such as Courier. | |
71 | +---+ A1 +---+ | |
72 | +R1 +------------+R2 | | |
73 | +-+-+- +--++ | |
74 | | -- -- | | |
75 | | -- A0 -- | | |
76 | A0| ---- | | |
77 | | ---- | A2 | |
78 | | -- -- | | |
79 | | -- -- | | |
80 | +-+-+- +-+-+ | |
81 | +R0 +-------------+R3 | | |
82 | +---+ A3 +---+ | |
83 | ||
84 | TESTCASES = | |
85 | 1. OSPF P2MP -Verify state change events on p2mp network. | |
86 | """ | |
87 | ||
88 | ||
dc5298d7 | 89 | def setup_module(mod): |
90 | """ | |
91 | Sets up the pytest environment | |
92 | ||
93 | * `mod`: module name | |
94 | """ | |
dc5298d7 | 95 | testsuite_run_time = time.asctime(time.localtime(time.time())) |
96 | logger.info("Testsuite start time: {}".format(testsuite_run_time)) | |
97 | logger.info("=" * 40) | |
98 | ||
99 | logger.info("Running setup_module to create topology") | |
100 | ||
101 | # This function initiates the topology build with Topogen... | |
e82b531d CH |
102 | json_file = "{}/ospf_p2mp.json".format(CWD) |
103 | tgen = Topogen(json_file, mod.__name__) | |
104 | global topo | |
105 | topo = tgen.json_topo | |
dc5298d7 | 106 | # ... and here it calls Mininet initialization functions. |
107 | ||
108 | # get list of daemons needs to be started for this suite. | |
109 | daemons = topo_daemons(tgen, topo) | |
110 | ||
111 | # Starting topology, create tmp files which are loaded to routers | |
d60a3f0e | 112 | # to start daemons and then start routers |
dc5298d7 | 113 | start_topology(tgen, daemons) |
114 | ||
115 | # Creating configuration from JSON | |
116 | build_config_from_json(tgen, topo) | |
117 | ||
118 | # Don't run this test if we have any failure. | |
119 | if tgen.routers_have_failure(): | |
120 | pytest.skip(tgen.errors) | |
121 | ||
dc5298d7 | 122 | logger.info("Running setup_module() done") |
123 | ||
124 | ||
125 | def teardown_module(mod): | |
126 | """ | |
127 | Teardown the pytest environment. | |
128 | ||
129 | * `mod`: module name | |
130 | """ | |
131 | ||
132 | logger.info("Running teardown_module to delete topology") | |
133 | ||
134 | tgen = get_topogen() | |
135 | ||
136 | # Stop toplogy and Remove tmp files | |
137 | tgen.stop_topology() | |
138 | ||
139 | logger.info( | |
140 | "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) | |
141 | ) | |
142 | logger.info("=" * 40) | |
143 | ||
144 | ||
145 | # ################################## | |
146 | # Test cases start here. | |
147 | # ################################## | |
148 | ||
149 | ||
150 | def test_ospf_p2mp_tc1_p0(request): | |
151 | """OSPF IFSM -Verify state change events on p2mp network.""" | |
152 | tc_name = request.node.name | |
153 | write_test_header(tc_name) | |
154 | tgen = get_topogen() | |
155 | ||
156 | # Don't run this test if we have any failure. | |
157 | if tgen.routers_have_failure(): | |
158 | pytest.skip(tgen.errors) | |
159 | ||
160 | global topo | |
161 | step("Bring up the base config as per the topology") | |
162 | reset_config_on_routers(tgen) | |
163 | step( | |
164 | "Verify that OSPF is subscribed to multi cast services " | |
165 | "(All SPF, all DR Routers)." | |
166 | ) | |
167 | step("Verify that interface is enabled in ospf.") | |
168 | step("Verify that config is successful.") | |
169 | dut = "r0" | |
170 | input_dict = { | |
171 | "r0": { | |
172 | "links": { | |
173 | "r3": {"ospf": {"mcastMemberOspfAllRouters": True, "ospfEnabled": True}} | |
174 | } | |
175 | } | |
176 | } | |
177 | result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) | |
178 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
179 | ||
180 | step("Delete the ip address") | |
181 | topo1 = { | |
182 | "r0": { | |
183 | "links": { | |
184 | "r3": { | |
185 | "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], | |
186 | "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], | |
187 | "delete": True, | |
188 | } | |
189 | } | |
190 | } | |
191 | } | |
192 | ||
193 | result = create_interfaces_cfg(tgen, topo1) | |
194 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
195 | ||
196 | step("Change the ip on the R0 interface") | |
197 | ||
198 | topo_modify_change_ip = deepcopy(topo) | |
199 | intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] | |
200 | topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str( | |
d7d21c3a | 201 | IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3 |
dc5298d7 | 202 | ) + "/{}".format(intf_ip.split("/")[1]) |
203 | ||
204 | build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) | |
205 | step("Verify that interface is enabled in ospf.") | |
206 | dut = "r0" | |
207 | input_dict = { | |
208 | "r0": { | |
209 | "links": { | |
210 | "r3": { | |
211 | "ospf": { | |
212 | "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ | |
213 | "r3" | |
214 | ]["ipv4"].split("/")[0], | |
215 | "ipAddressPrefixlen": int( | |
216 | topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ | |
217 | "ipv4" | |
218 | ].split("/")[1] | |
219 | ), | |
220 | } | |
221 | } | |
222 | } | |
223 | } | |
224 | } | |
225 | result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) | |
226 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
227 | ||
228 | step("Modify the mask on the R0 interface") | |
229 | ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] | |
230 | mask = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] | |
231 | step("Delete the ip address") | |
232 | topo1 = { | |
233 | "r0": { | |
234 | "links": { | |
235 | "r3": { | |
236 | "ipv4": ip_addr, | |
237 | "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], | |
238 | "delete": True, | |
239 | } | |
240 | } | |
241 | } | |
242 | } | |
243 | ||
244 | result = create_interfaces_cfg(tgen, topo1) | |
245 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
246 | ||
247 | step("Change the ip on the R0 interface") | |
248 | ||
249 | topo_modify_change_ip = deepcopy(topo) | |
250 | intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] | |
251 | topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str( | |
d7d21c3a | 252 | IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3 |
dc5298d7 | 253 | ) + "/{}".format(int(intf_ip.split("/")[1]) + 1) |
254 | ||
255 | build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) | |
256 | step("Verify that interface is enabled in ospf.") | |
257 | dut = "r0" | |
258 | input_dict = { | |
259 | "r0": { | |
260 | "links": { | |
261 | "r3": { | |
262 | "ospf": { | |
263 | "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ | |
264 | "r3" | |
265 | ]["ipv4"].split("/")[0], | |
266 | "ipAddressPrefixlen": int( | |
267 | topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ | |
268 | "ipv4" | |
269 | ].split("/")[1] | |
270 | ), | |
271 | } | |
272 | } | |
273 | } | |
274 | } | |
275 | } | |
276 | result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) | |
277 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
278 | ||
279 | topo1 = { | |
280 | "r0": { | |
281 | "links": { | |
282 | "r3": { | |
283 | "ipv4": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ | |
284 | "ipv4" | |
285 | ], | |
286 | "interface": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ | |
287 | "interface" | |
288 | ], | |
289 | "delete": True, | |
290 | } | |
291 | } | |
292 | } | |
293 | } | |
294 | ||
295 | result = create_interfaces_cfg(tgen, topo1) | |
296 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
297 | ||
298 | build_config_from_json(tgen, topo, save_bkup=False) | |
299 | ||
300 | step("Change the area id on the interface") | |
301 | input_dict = { | |
302 | "r0": { | |
303 | "links": { | |
304 | "r3": { | |
305 | "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], | |
306 | "ospf": {"area": "0.0.0.0"}, | |
307 | "delete": True, | |
308 | } | |
309 | } | |
310 | } | |
311 | } | |
312 | ||
313 | result = create_interfaces_cfg(tgen, input_dict) | |
314 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
315 | ||
316 | input_dict = { | |
317 | "r0": { | |
318 | "links": { | |
319 | "r3": { | |
320 | "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], | |
321 | "ospf": {"area": "0.0.0.1"}, | |
322 | } | |
323 | } | |
324 | } | |
325 | } | |
326 | ||
327 | result = create_interfaces_cfg(tgen, input_dict) | |
328 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
329 | step("Verify that interface is enabled in ospf.") | |
330 | dut = "r0" | |
331 | input_dict = { | |
332 | "r0": {"links": {"r3": {"ospf": {"area": "0.0.0.1", "ospfEnabled": True}}}} | |
333 | } | |
334 | result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) | |
335 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
336 | ||
337 | input_dict = { | |
338 | "r0": { | |
339 | "links": { | |
340 | "r3": { | |
341 | "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], | |
342 | "ospf": {"area": "0.0.0.1"}, | |
343 | "delete": True, | |
344 | } | |
345 | } | |
346 | } | |
347 | } | |
348 | ||
349 | result = create_interfaces_cfg(tgen, input_dict) | |
350 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
351 | ||
352 | input_dict = { | |
353 | "r0": { | |
354 | "links": { | |
355 | "r3": { | |
356 | "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], | |
357 | "ospf": {"area": "0.0.0.0"}, | |
358 | } | |
359 | } | |
360 | } | |
361 | } | |
362 | ||
363 | result = create_interfaces_cfg(tgen, input_dict) | |
364 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
365 | ||
366 | step("Verify if interface is enabled with network type P2MP") | |
367 | input_dict = { | |
368 | "r0": { | |
369 | "links": { | |
370 | "r3": { | |
371 | "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], | |
5980ad0a | 372 | "ospf": {"area": "0.0.0.0", "networkType": "POINTOMULTIPOINT"}, |
dc5298d7 | 373 | } |
374 | } | |
375 | } | |
376 | } | |
377 | result = create_interfaces_cfg(tgen, input_dict) | |
378 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
379 | ||
380 | write_test_footer(tc_name) | |
381 | ||
382 | ||
675ba67d LB |
383 | @retry(retry_timeout=30) |
384 | def verify_ospf_json(tgen, dut, input_dict, cmd="show ip ospf database json"): | |
385 | del tgen | |
386 | show_ospf_json = run_frr_cmd(dut, cmd, isjson=True) | |
387 | if not bool(show_ospf_json): | |
388 | return "ospf is not running" | |
389 | result = json_cmp(show_ospf_json, input_dict) | |
390 | return str(result) if result else None | |
391 | ||
392 | ||
393 | @pytest.mark.parametrize("tgen", [2], indirect=True) | |
394 | def test_ospf_nbrs(tgen): | |
395 | db_full = { | |
396 | "areas": { | |
397 | "0.0.0.0": { | |
398 | "routerLinkStates": [ | |
399 | { | |
400 | "lsId": "100.1.1.0", | |
401 | "advertisedRouter": "100.1.1.0", | |
402 | "numOfRouterLinks": 6, | |
403 | }, | |
404 | { | |
405 | "lsId": "100.1.1.1", | |
406 | "advertisedRouter": "100.1.1.1", | |
407 | "numOfRouterLinks": 6, | |
408 | }, | |
409 | { | |
410 | "lsId": "100.1.1.2", | |
411 | "advertisedRouter": "100.1.1.2", | |
412 | "numOfRouterLinks": 6, | |
413 | }, | |
414 | { | |
415 | "lsId": "100.1.1.3", | |
416 | "advertisedRouter": "100.1.1.3", | |
417 | "numOfRouterLinks": 7, | |
418 | }, | |
419 | ] | |
420 | } | |
421 | } | |
422 | } | |
423 | input = [ | |
424 | [ | |
425 | "r0", | |
426 | "show ip ospf n json", | |
427 | { | |
428 | "neighbors": { | |
429 | "100.1.1.1": [ | |
430 | { | |
431 | "state": "Full/DROther", | |
432 | } | |
433 | ], | |
434 | "100.1.1.2": [ | |
435 | { | |
436 | "state": "Full/DROther", | |
437 | } | |
438 | ], | |
439 | "100.1.1.3": [ | |
440 | { | |
441 | "state": "Full/DROther", | |
442 | } | |
443 | ], | |
444 | } | |
445 | }, | |
446 | ], | |
447 | [ | |
448 | "r1", | |
449 | "show ip ospf n json", | |
450 | { | |
451 | "neighbors": { | |
452 | "100.1.1.0": [ | |
453 | { | |
454 | "state": "Full/DROther", | |
455 | } | |
456 | ], | |
457 | "100.1.1.2": [ | |
458 | { | |
459 | "state": "Full/DROther", | |
460 | } | |
461 | ], | |
462 | "100.1.1.3": [ | |
463 | { | |
464 | "state": "Full/DROther", | |
465 | } | |
466 | ], | |
467 | } | |
468 | }, | |
469 | ], | |
470 | [ | |
471 | "r2", | |
472 | "show ip ospf n json", | |
473 | { | |
474 | "neighbors": { | |
475 | "100.1.1.0": [ | |
476 | { | |
477 | "state": "Full/DROther", | |
478 | } | |
479 | ], | |
480 | "100.1.1.1": [ | |
481 | { | |
482 | "state": "Full/DROther", | |
483 | } | |
484 | ], | |
485 | "100.1.1.3": [ | |
486 | { | |
487 | "state": "Full/DROther", | |
488 | } | |
489 | ], | |
490 | } | |
491 | }, | |
492 | ], | |
493 | [ | |
494 | "r3", | |
495 | "show ip ospf n json", | |
496 | { | |
497 | "neighbors": { | |
498 | "100.1.1.0": [ | |
499 | { | |
500 | "state": "Full/DROther", | |
501 | } | |
502 | ], | |
503 | "100.1.1.1": [ | |
504 | { | |
505 | "state": "Full/DROther", | |
506 | } | |
507 | ], | |
508 | "100.1.1.2": [ | |
509 | { | |
510 | "state": "Full/DROther", | |
511 | } | |
512 | ], | |
513 | } | |
514 | }, | |
515 | ], | |
516 | ["r0", "show ip ospf database json", db_full], | |
517 | ["r1", "show ip ospf database json", db_full], | |
518 | ["r2", "show ip ospf database json", db_full], | |
519 | ["r3", "show ip ospf database json", db_full], | |
520 | ["r0", "show ip ospf database json", db_full], | |
521 | ["r0", "show ip ospf database router json", {}], | |
522 | ["r0", "show ip ospf interface traffic json", {}], | |
523 | ["r1", "show ip ospf interface traffic json", {}], | |
524 | ["r2", "show ip ospf interface traffic json", {}], | |
525 | ["r3", "show ip ospf interface traffic json", {}], | |
526 | ] | |
527 | for cmd_set in input: | |
528 | step("test_ospf: %s - %s" % (cmd_set[0], cmd_set[1])) | |
529 | assert ( | |
530 | verify_ospf_json(tgen, tgen.gears[cmd_set[0]], cmd_set[2], cmd_set[1]) | |
531 | is None | |
532 | ) | |
533 | ||
534 | ||
dc5298d7 | 535 | if __name__ == "__main__": |
536 | args = ["-s"] + sys.argv[1:] | |
537 | sys.exit(pytest.main(args)) |