]> git.proxmox.com Git - mirror_frr.git/blame - tests/topotests/lib/ospf.py
Merge pull request #7755 from mjstapp/fix_rnh_via_default
[mirror_frr.git] / tests / topotests / lib / ospf.py
CommitLineData
4256a209 1#
2# Copyright (c) 2020 by VMware, Inc. ("VMware")
3# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
4# ("NetDEF") in this file.
5#
6# Permission to use, copy, modify, and/or distribute this software
7# for any purpose with or without fee is hereby granted, provided
8# that the above copyright notice and this permission notice appear
9# in all copies.
10#
11# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
12# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
14# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
15# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
16# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
17# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
18# OF THIS SOFTWARE.
19#
20
21from copy import deepcopy
22import traceback
23from time import sleep
24from lib.topolog import logger
25import ipaddr
61196140 26from lib.topotest import frr_unicode
4256a209 27
28# Import common_config to use commomnly used APIs
701a0192 29from lib.common_config import (
30 create_common_configuration,
31 InvalidCLIError,
32 retry,
33 generate_ips,
34 check_address_types,
35 validate_ip_address,
36 run_frr_cmd,
37)
4256a209 38
39LOGDIR = "/tmp/topotests/"
40TMPDIR = None
41
42################################
43# Configure procs
44################################
45
701a0192 46
47def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=True):
4256a209 48 """
49 API to configure ospf on router.
50
51 Parameters
52 ----------
53 * `tgen` : Topogen object
54 * `topo` : json file data
55 * `input_dict` : Input dict data, required when configuring from testcase
56 * `build` : Only for initial setup phase this is set as True.
57 * `load_config` : Loading the config to router this is set as True.
58
59 Usage
60 -----
61 input_dict = {
62 "r1": {
63 "ospf": {
64 "router_id": "22.22.22.22",
65 "area": [{ "id":0.0.0.0, "type": "nssa"}]
66 }
67 }
68
69 result = create_router_ospf(tgen, topo, input_dict)
70
71 Returns
72 -------
73 True or False
74 """
75 logger.debug("Entering lib API: create_router_ospf()")
76 result = False
77
78 if not input_dict:
79 input_dict = deepcopy(topo)
80 else:
81 topo = topo["routers"]
82 input_dict = deepcopy(input_dict)
83
84 for router in input_dict.keys():
85 if "ospf" not in input_dict[router]:
86 logger.debug("Router %s: 'ospf' not present in input_dict", router)
87 continue
88
701a0192 89 result = __create_ospf_global(tgen, input_dict, router, build, load_config)
4256a209 90 if result is True:
91 ospf_data = input_dict[router]["ospf"]
92
4256a209 93 logger.debug("Exiting lib API: create_router_ospf()")
94 return result
95
96
701a0192 97def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True):
4256a209 98 """
99 Helper API to create ospf global configuration.
100
101 Parameters
102 ----------
103 * `tgen` : Topogen object
104 * `input_dict` : Input dict data, required when configuring from testcase
105 * `router` : router to be configured.
106 * `build` : Only for initial setup phase this is set as True.
107 * `load_config` : Loading the config to router this is set as True.
108
109 Returns
110 -------
111 True or False
112 """
113
114 result = False
115 logger.debug("Entering lib API: __create_ospf_global()")
116 try:
117
118 ospf_data = input_dict[router]["ospf"]
119 del_ospf_action = ospf_data.setdefault("delete", False)
120 if del_ospf_action:
121 config_data = ["no router ospf"]
701a0192 122 result = create_common_configuration(
123 tgen, router, config_data, "ospf", build, load_config
124 )
4256a209 125 return result
126
127 config_data = []
128 cmd = "router ospf"
129
130 config_data.append(cmd)
131
132 # router id
133 router_id = ospf_data.setdefault("router_id", None)
134 del_router_id = ospf_data.setdefault("del_router_id", False)
135 if del_router_id:
136 config_data.append("no ospf router-id")
137 if router_id:
701a0192 138 config_data.append("ospf router-id {}".format(router_id))
4256a209 139
140 # redistribute command
141 redistribute_data = ospf_data.setdefault("redistribute", {})
142 if redistribute_data:
143 for redistribute in redistribute_data:
144 if "redist_type" not in redistribute:
701a0192 145 logger.debug(
146 "Router %s: 'redist_type' not present in " "input_dict", router
147 )
4256a209 148 else:
701a0192 149 cmd = "redistribute {}".format(redistribute["redist_type"])
4256a209 150 for red_type in redistribute_data:
151 if "route_map" in red_type:
701a0192 152 cmd = cmd + " route-map {}".format(red_type["route_map"])
4256a209 153 del_action = redistribute.setdefault("delete", False)
154 if del_action:
155 cmd = "no {}".format(cmd)
156 config_data.append(cmd)
701a0192 157 # area information
4256a209 158 area_data = ospf_data.setdefault("area", {})
159 if area_data:
160 for area in area_data:
161 if "id" not in area:
701a0192 162 logger.debug(
163 "Router %s: 'area id' not present in " "input_dict", router
164 )
4256a209 165 else:
166 cmd = "area {}".format(area["id"])
167
168 if "type" in area:
169 cmd = cmd + " {}".format(area["type"])
170
171 del_action = area.setdefault("delete", False)
172 if del_action:
173 cmd = "no {}".format(cmd)
174 config_data.append(cmd)
701a0192 175 result = create_common_configuration(
176 tgen, router, config_data, "ospf", build, load_config
177 )
4256a209 178
179 # summary information
180 summary_data = ospf_data.setdefault("summary-address", {})
181 if summary_data:
182 for summary in summary_data:
183 if "prefix" not in summary:
701a0192 184 logger.debug(
185 "Router %s: 'summary-address' not present in " "input_dict",
186 router,
187 )
4256a209 188 else:
701a0192 189 cmd = "summary {}/{}".format(summary["prefix"], summary["mask"])
4256a209 190
191 _tag = summary.setdefault("tag", None)
192 if _tag:
193 cmd = "{} tag {}".format(cmd, _tag)
194
195 _advertise = summary.setdefault("advertise", True)
196 if not _advertise:
197 cmd = "{} no-advertise".format(cmd)
198
199 del_action = summary.setdefault("delete", False)
200 if del_action:
201 cmd = "no {}".format(cmd)
202 config_data.append(cmd)
701a0192 203 result = create_common_configuration(
204 tgen, router, config_data, "ospf", build, load_config
205 )
4256a209 206
207 except InvalidCLIError:
208 # Traceback
209 errormsg = traceback.format_exc()
210 logger.error(errormsg)
211 return errormsg
212
213 logger.debug("Exiting lib API: create_ospf_global()")
214 return result
215
216
701a0192 217def create_router_ospf6(tgen, topo, input_dict=None, build=False, load_config=True):
4256a209 218 """
219 API to configure ospf on router
220
221 Parameters
222 ----------
223 * `tgen` : Topogen object
224 * `topo` : json file data
225 * `input_dict` : Input dict data, required when configuring from testcase
226 * `build` : Only for initial setup phase this is set as True.
227
228 Usage
229 -----
230 input_dict = {
231 "r1": {
232 "ospf6": {
233 "router_id": "22.22.22.22",
234 }
235 }
236
237 Returns
238 -------
239 True or False
240 """
241 logger.debug("Entering lib API: create_router_ospf()")
242 result = False
243
244 if not input_dict:
245 input_dict = deepcopy(topo)
246 else:
247 topo = topo["routers"]
248 input_dict = deepcopy(input_dict)
249 for router in input_dict.keys():
250 if "ospf" not in input_dict[router]:
251 logger.debug("Router %s: 'ospf' not present in input_dict", router)
252 continue
253
701a0192 254 result = __create_ospf_global(tgen, input_dict, router, build, load_config)
4256a209 255
256 logger.debug("Exiting lib API: create_router_ospf()")
257 return result
258
259
701a0192 260def __create_ospf6_global(tgen, input_dict, router, build=False, load_config=True):
4256a209 261 """
262 Helper API to create ospf global configuration.
263
264 Parameters
265 ----------
266 * `tgen` : Topogen object
267 * `input_dict` : Input dict data, required when configuring from testcase
268 * `router` : router id to be configured.
269 * `build` : Only for initial setup phase this is set as True.
270
271 Returns
272 -------
273 True or False
274 """
275
276 result = False
277 logger.debug("Entering lib API: __create_ospf_global()")
278 try:
279
280 ospf_data = input_dict[router]["ospf6"]
281 del_ospf_action = ospf_data.setdefault("delete", False)
282 if del_ospf_action:
283 config_data = ["no ipv6 router ospf"]
701a0192 284 result = create_common_configuration(
285 tgen, router, config_data, "ospf", build, load_config
286 )
4256a209 287 return result
288
289 config_data = []
290 cmd = "router ospf"
291
292 config_data.append(cmd)
293
294 router_id = ospf_data.setdefault("router_id", None)
295 del_router_id = ospf_data.setdefault("del_router_id", False)
296 if del_router_id:
297 config_data.append("no ospf router-id")
298 if router_id:
701a0192 299 config_data.append("ospf router-id {}".format(router_id))
4256a209 300
701a0192 301 result = create_common_configuration(
302 tgen, router, config_data, "ospf", build, load_config
303 )
4256a209 304 except InvalidCLIError:
305 # Traceback
306 errormsg = traceback.format_exc()
307 logger.error(errormsg)
308 return errormsg
309
310 logger.debug("Exiting lib API: create_ospf_global()")
311 return result
312
701a0192 313
314def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config=True):
4256a209 315 """
316 API to configure ospf on router.
317
318 Parameters
319 ----------
320 * `tgen` : Topogen object
321 * `topo` : json file data
322 * `input_dict` : Input dict data, required when configuring from testcase
323 * `build` : Only for initial setup phase this is set as True.
324 * `load_config` : Loading the config to router this is set as True.
325
326 Usage
327 -----
328 r1_ospf_auth = {
329 "r1": {
330 "links": {
331 "r2": {
332 "ospf": {
333 "authentication": 'message-digest',
334 "authentication-key": "ospf",
335 "message-digest-key": "10"
336 }
337 }
338 }
339 }
340 }
341 result = config_ospf_interface(tgen, topo, r1_ospf_auth)
342
343 Returns
344 -------
345 True or False
346 """
347 logger.debug("Enter lib config_ospf_interface")
348 if not input_dict:
349 input_dict = deepcopy(topo)
350 else:
351 input_dict = deepcopy(input_dict)
352 for router in input_dict.keys():
353 config_data = []
701a0192 354 for lnk in input_dict[router]["links"].keys():
355 if "ospf" not in input_dict[router]["links"][lnk]:
356 logger.debug(
357 "Router %s: ospf configs is not present in"
358 "input_dict, passed input_dict",
359 router,
360 input_dict,
361 )
4256a209 362 continue
701a0192 363 ospf_data = input_dict[router]["links"][lnk]["ospf"]
4256a209 364 data_ospf_area = ospf_data.setdefault("area", None)
365 data_ospf_auth = ospf_data.setdefault("authentication", None)
366 data_ospf_dr_priority = ospf_data.setdefault("priority", None)
367 data_ospf_cost = ospf_data.setdefault("cost", None)
368
369 try:
701a0192 370 intf = topo["routers"][router]["links"][lnk]["interface"]
4256a209 371 except KeyError:
701a0192 372 intf = topo["switches"][router]["links"][lnk]["interface"]
4256a209 373
374 # interface
375 cmd = "interface {}".format(intf)
376
377 config_data.append(cmd)
378 # interface area config
379 if data_ospf_area:
380 cmd = "ip ospf area {}".format(data_ospf_area)
381 config_data.append(cmd)
382 # interface ospf auth
383 if data_ospf_auth:
701a0192 384 if data_ospf_auth == "null":
4256a209 385 cmd = "ip ospf authentication null"
701a0192 386 elif data_ospf_auth == "message-digest":
4256a209 387 cmd = "ip ospf authentication message-digest"
388 else:
389 cmd = "ip ospf authentication"
390
701a0192 391 if "del_action" in ospf_data:
4256a209 392 cmd = "no {}".format(cmd)
393 config_data.append(cmd)
394
395 if "message-digest-key" in ospf_data:
396 cmd = "ip ospf message-digest-key {} md5 {}".format(
701a0192 397 ospf_data["message-digest-key"], ospf_data["authentication-key"]
398 )
399 if "del_action" in ospf_data:
4256a209 400 cmd = "no {}".format(cmd)
401 config_data.append(cmd)
402
701a0192 403 if (
404 "authentication-key" in ospf_data
405 and "message-digest-key" not in ospf_data
406 ):
407 cmd = "ip ospf authentication-key {}".format(
408 ospf_data["authentication-key"]
409 )
410 if "del_action" in ospf_data:
4256a209 411 cmd = "no {}".format(cmd)
412 config_data.append(cmd)
413
414 # interface ospf dr priority
415 if data_ospf_dr_priority in ospf_data:
701a0192 416 cmd = "ip ospf priority {}".format(ospf_data["priority"])
417 if "del_action" in ospf_data:
4256a209 418 cmd = "no {}".format(cmd)
419 config_data.append(cmd)
420
421 # interface ospf cost
422 if data_ospf_cost in ospf_data:
701a0192 423 cmd = "ip ospf cost {}".format(ospf_data["cost"])
424 if "del_action" in ospf_data:
4256a209 425 cmd = "no {}".format(cmd)
426 config_data.append(cmd)
427
428 if build:
429 return config_data
430 else:
701a0192 431 result = create_common_configuration(
432 tgen, router, config_data, "interface_config", build=build
433 )
4256a209 434 logger.debug("Exiting lib API: create_igmp_config()")
435 return result
436
701a0192 437
4256a209 438def clear_ospf(tgen, router):
439 """
440 This API is to clear ospf neighborship by running
441 clear ip ospf interface * command,
442
443 Parameters
444 ----------
445 * `tgen`: topogen object
446 * `router`: device under test
447
448 Usage
449 -----
450 clear_ospf(tgen, "r1")
451 """
452
453 logger.debug("Entering lib API: clear_ospf()")
454 if router not in tgen.routers():
455 return False
456
457 rnode = tgen.routers()[router]
458
459 # Clearing OSPF
460 logger.info("Clearing ospf process for router %s..", router)
461
462 run_frr_cmd(rnode, "clear ip ospf interface ")
463
464 logger.debug("Exiting lib API: clear_ospf()")
465
466
467################################
468# Verification procs
469################################
470@retry(attempts=40, wait=2, return_is_str=True)
471def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
472 """
473 This API is to verify ospf neighborship by running
474 show ip ospf neighbour command,
475
476 Parameters
477 ----------
478 * `tgen` : Topogen object
479 * `topo` : json file data
480 * `dut`: device under test
481 * `input_dict` : Input dict data, required when configuring from testcase
482 * `lan` : verify neighbors in lan topology
483
484 Usage
485 -----
486 1. To check FULL neighbors.
487 verify_ospf_neighbor(tgen, topo, dut=dut)
488
489 2. To check neighbors with their roles.
490 input_dict = {
491 "r0": {
492 "ospf": {
493 "neighbors": {
494 "r1": {
495 "state": "Full",
496 "role": "DR"
497 },
498 "r2": {
499 "state": "Full",
500 "role": "DROther"
501 },
502 "r3": {
503 "state": "Full",
504 "role": "DROther"
505 }
506 }
507 }
508 }
509 }
510 result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
511
512 Returns
513 -------
514 True or False (Error Message)
515 """
516 logger.debug("Entering lib API: verify_ospf_neighbor()")
517 result = False
518 if input_dict:
11761ab0 519 for router, rnode in tgen.routers().items():
701a0192 520 if "ospf" not in topo["routers"][router]:
4256a209 521 continue
522
523 if dut is not None and dut != router:
524 continue
525
526 logger.info("Verifying OSPF neighborship on router %s:", router)
701a0192 527 show_ospf_json = run_frr_cmd(
528 rnode, "show ip ospf neighbor all json", isjson=True
529 )
4256a209 530
531 # Verifying output dictionary show_ospf_json is empty or not
532 if not bool(show_ospf_json):
533 errormsg = "OSPF is not running"
534 return errormsg
535
536 ospf_data_list = input_dict[router]["ospf"]
701a0192 537 ospf_nbr_list = ospf_data_list["neighbors"]
4256a209 538
539 for ospf_nbr, nbr_data in ospf_nbr_list.items():
701a0192 540 data_ip = topo["routers"][ospf_nbr]["links"]
541 data_rid = topo["routers"][ospf_nbr]["ospf"]["router_id"]
4256a209 542 if ospf_nbr in data_ip:
543 nbr_details = nbr_data[ospf_nbr]
544 elif lan:
701a0192 545 for switch in topo["switches"]:
546 if "ospf" in topo["switches"][switch]["links"][router]:
547 neighbor_ip = data_ip[switch]["ipv4"].split("/")[0]
4256a209 548 else:
549 continue
550 else:
701a0192 551 neighbor_ip = data_ip[router]["ipv4"].split("/")[0]
4256a209 552
553 nh_state = None
554 neighbor_ip = neighbor_ip.lower()
555 nbr_rid = data_rid
556 try:
701a0192 557 nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0]
558 intf_state = show_ospf_json[nbr_rid][0]["state"].split("/")[1]
4256a209 559 except KeyError:
701a0192 560 errormsg = "[DUT: {}] OSPF peer {} missing".format(router, nbr_rid)
4256a209 561 return errormsg
562
701a0192 563 nbr_state = nbr_data.setdefault("state", None)
564 nbr_role = nbr_data.setdefault("role", None)
4256a209 565
566 if nbr_state:
567 if nbr_state == nh_state:
701a0192 568 logger.info(
569 "[DUT: {}] OSPF Nbr is {}:{} State {}".format(
570 router, ospf_nbr, nbr_rid, nh_state
571 )
572 )
4256a209 573 result = True
574 else:
701a0192 575 errormsg = (
576 "[DUT: {}] OSPF is not Converged, neighbor"
577 " state is {}".format(router, nh_state)
578 )
4256a209 579 return errormsg
580 if nbr_role:
581 if nbr_role == intf_state:
701a0192 582 logger.info(
583 "[DUT: {}] OSPF Nbr is {}: {} Role {}".format(
584 router, ospf_nbr, nbr_rid, nbr_role
585 )
586 )
4256a209 587 else:
701a0192 588 errormsg = (
589 "[DUT: {}] OSPF is not Converged with rid"
590 "{}, role is {}".format(router, nbr_rid, intf_state)
591 )
4256a209 592 return errormsg
593 continue
594 else:
11761ab0 595 for router, rnode in tgen.routers().items():
701a0192 596 if "ospf" not in topo["routers"][router]:
4256a209 597 continue
598
599 if dut is not None and dut != router:
600 continue
601
602 logger.info("Verifying OSPF neighborship on router %s:", router)
701a0192 603 show_ospf_json = run_frr_cmd(
604 rnode, "show ip ospf neighbor all json", isjson=True
605 )
4256a209 606 # Verifying output dictionary show_ospf_json is empty or not
607 if not bool(show_ospf_json):
608 errormsg = "OSPF is not running"
609 return errormsg
610
611 ospf_data_list = topo["routers"][router]["ospf"]
701a0192 612 ospf_neighbors = ospf_data_list["neighbors"]
4256a209 613 total_peer = 0
614 total_peer = len(ospf_neighbors.keys())
615 no_of_ospf_nbr = 0
701a0192 616 ospf_nbr_list = ospf_data_list["neighbors"]
4256a209 617 no_of_peer = 0
618 for ospf_nbr, nbr_data in ospf_nbr_list.items():
619 if nbr_data:
701a0192 620 data_ip = topo["routers"][nbr_data["nbr"]]["links"]
621 data_rid = topo["routers"][nbr_data["nbr"]]["ospf"]["router_id"]
4256a209 622 else:
701a0192 623 data_ip = topo["routers"][ospf_nbr]["links"]
624 data_rid = topo["routers"][ospf_nbr]["ospf"]["router_id"]
4256a209 625 if ospf_nbr in data_ip:
626 nbr_details = nbr_data[ospf_nbr]
627 elif lan:
701a0192 628 for switch in topo["switches"]:
629 if "ospf" in topo["switches"][switch]["links"][router]:
630 neighbor_ip = data_ip[switch]["ipv4"].split("/")[0]
4256a209 631 else:
632 continue
633 else:
701a0192 634 neighbor_ip = data_ip[router]["ipv4"].split("/")[0]
4256a209 635
636 nh_state = None
637 neighbor_ip = neighbor_ip.lower()
638 nbr_rid = data_rid
639 try:
701a0192 640 nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0]
4256a209 641 except KeyError:
701a0192 642 errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
643 router, nbr_rid, ospf_nbr
644 )
4256a209 645 return errormsg
646
701a0192 647 if nh_state == "Full":
4256a209 648 no_of_peer += 1
649
650 if no_of_peer == total_peer:
651 logger.info("[DUT: {}] OSPF is Converged".format(router))
652 result = True
653 else:
701a0192 654 errormsg = "[DUT: {}] OSPF is not Converged".format(router)
4256a209 655 return errormsg
656
657 logger.debug("Exiting API: verify_ospf_neighbor()")
658 return result
659
701a0192 660
b29a56b3 661@retry(attempts=21, wait=2, return_is_str=True)
701a0192 662def verify_ospf_rib(
663 tgen, dut, input_dict, next_hop=None, tag=None, metric=None, fib=None
664):
4256a209 665 """
666 This API is to verify ospf routes by running
667 show ip ospf route command.
668
669 Parameters
670 ----------
671 * `tgen` : Topogen object
672 * `dut`: device under test
673 * `input_dict` : Input dict data, required when configuring from testcase
674 * `next_hop` : next to be verified
675 * `tag` : tag to be verified
676 * `metric` : metric to be verified
677 * `fib` : True if the route is installed in FIB.
678
679 Usage
680 -----
681 input_dict = {
682 "r1": {
683 "static_routes": [
684 {
685 "network": ip_net,
686 "no_of_ip": 1,
687 "routeType": "N"
688 }
689 ]
690 }
691 }
692
693 result = verify_ospf_rib(tgen, dut, input_dict,next_hop=nh)
694
695 Returns
696 -------
697 True or False (Error Message)
698 """
699
700 logger.info("Entering lib API: verify_ospf_rib()")
701 result = False
702 router_list = tgen.routers()
703 additional_nexthops_in_required_nhs = []
704 found_hops = []
705 for routerInput in input_dict.keys():
11761ab0 706 for router, rnode in router_list.items():
4256a209 707 if router != dut:
708 continue
709
710 logger.info("Checking router %s RIB:", router)
711
712 # Verifying RIB routes
713 command = "show ip ospf route"
714
715 found_routes = []
716 missing_routes = []
717
701a0192 718 if (
719 "static_routes" in input_dict[routerInput]
720 or "prefix" in input_dict[routerInput]
721 ):
4256a209 722 if "prefix" in input_dict[routerInput]:
723 static_routes = input_dict[routerInput]["prefix"]
724 else:
725 static_routes = input_dict[routerInput]["static_routes"]
726
4256a209 727 for static_route in static_routes:
728 cmd = "{}".format(command)
729
730 cmd = "{} json".format(cmd)
731
701a0192 732 ospf_rib_json = run_frr_cmd(rnode, cmd, isjson=True)
4256a209 733
734 # Verifying output dictionary ospf_rib_json is not empty
735 if bool(ospf_rib_json) is False:
701a0192 736 errormsg = (
737 "[DUT: {}] No routes found in OSPF route "
4256a209 738 "table".format(router)
701a0192 739 )
4256a209 740 return errormsg
741
742 network = static_route["network"]
743 no_of_ip = static_route.setdefault("no_of_ip", 1)
744 _tag = static_route.setdefault("tag", None)
745 _rtype = static_route.setdefault("routeType", None)
746
4256a209 747 # Generating IPs for verification
748 ip_list = generate_ips(network, no_of_ip)
749 st_found = False
750 nh_found = False
751
752 for st_rt in ip_list:
61196140 753 st_rt = str(ipaddr.IPNetwork(frr_unicode(st_rt)))
4256a209 754
755 _addr_type = validate_ip_address(st_rt)
701a0192 756 if _addr_type != "ipv4":
4256a209 757 continue
758
759 if st_rt in ospf_rib_json:
760 st_found = True
761 found_routes.append(st_rt)
762
763 if fib and next_hop:
764 if type(next_hop) is not list:
765 next_hop = [next_hop]
766
767 for mnh in range(0, len(ospf_rib_json[st_rt])):
701a0192 768 if (
769 "fib"
770 in ospf_rib_json[st_rt][mnh]["nexthops"][0]
771 ):
772 found_hops.append(
773 [
774 rib_r["ip"]
775 for rib_r in ospf_rib_json[st_rt][mnh][
776 "nexthops"
777 ]
778 ]
779 )
4256a209 780
781 if found_hops[0]:
701a0192 782 missing_list_of_nexthops = set(
783 found_hops[0]
784 ).difference(next_hop)
785 additional_nexthops_in_required_nhs = set(
786 next_hop
787 ).difference(found_hops[0])
4256a209 788
789 if additional_nexthops_in_required_nhs:
790 logger.info(
791 "Nexthop "
792 "%s is not active for route %s in "
793 "RIB of router %s\n",
794 additional_nexthops_in_required_nhs,
701a0192 795 st_rt,
796 dut,
797 )
4256a209 798 errormsg = (
799 "Nexthop {} is not active"
800 " for route {} in RIB of router"
801 " {}\n".format(
701a0192 802 additional_nexthops_in_required_nhs,
803 st_rt,
804 dut,
805 )
806 )
4256a209 807 return errormsg
808 else:
809 nh_found = True
810
811 elif next_hop and fib is None:
812 if type(next_hop) is not list:
813 next_hop = [next_hop]
701a0192 814 found_hops = [
815 rib_r["ip"]
816 for rib_r in ospf_rib_json[st_rt]["nexthops"]
817 ]
4256a209 818
819 if found_hops:
701a0192 820 missing_list_of_nexthops = set(
821 found_hops
822 ).difference(next_hop)
823 additional_nexthops_in_required_nhs = set(
824 next_hop
825 ).difference(found_hops)
4256a209 826
827 if additional_nexthops_in_required_nhs:
828 logger.info(
701a0192 829 "Missing nexthop %s for route"
830 " %s in RIB of router %s\n",
4256a209 831 additional_nexthops_in_required_nhs,
701a0192 832 st_rt,
833 dut,
834 )
835 errormsg = (
836 "Nexthop {} is Missing for "
837 "route {} in RIB of router {}\n".format(
838 additional_nexthops_in_required_nhs,
839 st_rt,
840 dut,
841 )
842 )
4256a209 843 return errormsg
844 else:
845 nh_found = True
846 if _rtype:
701a0192 847 if "routeType" not in ospf_rib_json[st_rt]:
848 errormsg = (
849 "[DUT: {}]: routeType missing"
850 "for route {} in OSPF RIB \n".format(dut, st_rt)
851 )
4256a209 852 return errormsg
701a0192 853 elif _rtype != ospf_rib_json[st_rt]["routeType"]:
854 errormsg = (
855 "[DUT: {}]: routeType mismatch"
856 "for route {} in OSPF RIB \n".format(dut, st_rt)
857 )
4256a209 858 return errormsg
859 else:
701a0192 860 logger.info(
861 "DUT: {}]: Found routeType {}"
862 "for route {}".format(dut, _rtype, st_rt)
863 )
4256a209 864 if tag:
701a0192 865 if "tag" not in ospf_rib_json[st_rt]:
866 errormsg = (
867 "[DUT: {}]: tag is not"
868 " present for"
869 " route {} in RIB \n".format(dut, st_rt)
870 )
4256a209 871 return errormsg
872
701a0192 873 if _tag != ospf_rib_json[st_rt]["tag"]:
874 errormsg = (
875 "[DUT: {}]: tag value {}"
876 " is not matched for"
877 " route {} in RIB \n".format(dut, _tag, st_rt,)
878 )
4256a209 879 return errormsg
880
881 if metric is not None:
701a0192 882 if "type2cost" not in ospf_rib_json[st_rt]:
883 errormsg = (
884 "[DUT: {}]: metric is"
885 " not present for"
886 " route {} in RIB \n".format(dut, st_rt)
887 )
4256a209 888 return errormsg
889
701a0192 890 if metric != ospf_rib_json[st_rt]["type2cost"]:
891 errormsg = (
892 "[DUT: {}]: metric value "
893 "{} is not matched for "
894 "route {} in RIB \n".format(dut, metric, st_rt,)
895 )
4256a209 896 return errormsg
897
898 else:
899 missing_routes.append(st_rt)
900
901 if nh_found:
701a0192 902 logger.info(
903 "[DUT: {}]: Found next_hop {} for all OSPF"
904 " routes in RIB".format(router, next_hop)
905 )
4256a209 906
907 if len(missing_routes) > 0:
701a0192 908 errormsg = "[DUT: {}]: Missing route in RIB, " "routes: {}".format(
909 dut, missing_routes
910 )
4256a209 911 return errormsg
912
913 if found_routes:
701a0192 914 logger.info(
915 "[DUT: %s]: Verified routes in RIB, found" " routes are: %s\n",
916 dut,
917 found_routes,
918 )
4256a209 919 result = True
920
921 logger.info("Exiting lib API: verify_ospf_rib()")
922 return result
923
924
925@retry(attempts=10, wait=2, return_is_str=True)
701a0192 926def verify_ospf_interface(tgen, topo, dut=None, lan=False, input_dict=None):
4256a209 927 """
928 This API is to verify ospf routes by running
929 show ip ospf interface command.
930
931 Parameters
932 ----------
933 * `tgen` : Topogen object
934 * `topo` : topology descriptions
935 * `dut`: device under test
936 * `lan`: if set to true this interface belongs to LAN.
937 * `input_dict` : Input dict data, required when configuring from testcase
938
939 Usage
940 -----
941 input_dict= {
942 'r0': {
943 'links':{
944 's1': {
945 'ospf':{
946 'priority':98,
947 'timerDeadSecs': 4,
948 'area': '0.0.0.3',
949 'mcastMemberOspfDesignatedRouters': True,
950 'mcastMemberOspfAllRouters': True,
951 'ospfEnabled': True,
952
953 }
954 }
955 }
956 }
957 }
958 result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
959
960 Returns
961 -------
962 True or False (Error Message)
963 """
964
965 logger.debug("Entering lib API: verify_ospf_interface()")
966 result = False
11761ab0 967 for router, rnode in tgen.routers().items():
701a0192 968 if "ospf" not in topo["routers"][router]:
4256a209 969 continue
970
971 if dut is not None and dut != router:
972 continue
973
974 logger.info("Verifying OSPF interface on router %s:", router)
701a0192 975 show_ospf_json = run_frr_cmd(rnode, "show ip ospf interface json", isjson=True)
4256a209 976
977 # Verifying output dictionary show_ospf_json is empty or not
978 if not bool(show_ospf_json):
979 errormsg = "OSPF is not running"
980 return errormsg
981
982 # To find neighbor ip type
983 ospf_intf_data = input_dict[router]["links"]
984 for ospf_intf, intf_data in ospf_intf_data.items():
701a0192 985 intf = topo["routers"][router]["links"][ospf_intf]["interface"]
986 if intf in show_ospf_json["interfaces"]:
987 for intf_attribute in intf_data["ospf"]:
988 if (
989 intf_data["ospf"][intf_attribute]
990 == show_ospf_json["interfaces"][intf][intf_attribute]
991 ):
992 logger.info(
993 "[DUT: %s] OSPF interface %s: %s is %s",
994 router,
995 intf,
996 intf_attribute,
997 intf_data["ospf"][intf_attribute],
998 )
4256a209 999 else:
701a0192 1000 errormsg = "[DUT: {}] OSPF interface {}: {} is {}, \
1001 Expected is {}".format(
1002 router,
1003 intf,
1004 intf_attribute,
1005 intf_data["ospf"][intf_attribute],
1006 show_ospf_json["interfaces"][intf][intf_attribute],
1007 )
4256a209 1008 return errormsg
1009 result = True
1010 logger.debug("Exiting API: verify_ospf_interface()")
1011 return result
1012
1013
b29a56b3 1014@retry(attempts=11, wait=2, return_is_str=True)
4256a209 1015def verify_ospf_database(tgen, topo, dut, input_dict):
1016 """
1017 This API is to verify ospf lsa's by running
1018 show ip ospf database command.
1019
1020 Parameters
1021 ----------
1022 * `tgen` : Topogen object
1023 * `dut`: device under test
1024 * `input_dict` : Input dict data, required when configuring from testcase
1025 * `topo` : next to be verified
1026
1027 Usage
1028 -----
1029 input_dict = {
1030 "areas": {
1031 "0.0.0.0": {
1032 "Router Link States": {
1033 "100.1.1.0-100.1.1.0": {
1034 "LSID": "100.1.1.0",
1035 "Advertised router": "100.1.1.0",
1036 "LSA Age": 130,
1037 "Sequence Number": "80000006",
1038 "Checksum": "a703",
1039 "Router links": 3
1040 }
1041 },
1042 "Net Link States": {
1043 "10.0.0.2-100.1.1.1": {
1044 "LSID": "10.0.0.2",
1045 "Advertised router": "100.1.1.1",
1046 "LSA Age": 137,
1047 "Sequence Number": "80000001",
1048 "Checksum": "9583"
1049 }
1050 },
1051 },
1052 }
1053 }
1054 result = verify_ospf_database(tgen, topo, dut, input_dict)
1055
1056 Returns
1057 -------
1058 True or False (Error Message)
1059 """
1060
1061 result = False
1062 router = dut
1063 logger.debug("Entering lib API: verify_ospf_database()")
1064
701a0192 1065 if "ospf" not in topo["routers"][dut]:
1066 errormsg = "[DUT: {}] OSPF is not configured on the router.".format(dut)
4256a209 1067 return errormsg
1068
1069 rnode = tgen.routers()[dut]
1070
1071 logger.info("Verifying OSPF interface on router %s:", dut)
701a0192 1072 show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json", isjson=True)
4256a209 1073 # Verifying output dictionary show_ospf_json is empty or not
1074 if not bool(show_ospf_json):
1075 errormsg = "OSPF is not running"
1076 return errormsg
1077
1078 # for inter and inter lsa's
1079 ospf_db_data = input_dict.setdefault("areas", None)
701a0192 1080 ospf_external_lsa = input_dict.setdefault("AS External Link States", None)
4256a209 1081 if ospf_db_data:
701a0192 1082 for ospf_area, area_lsa in ospf_db_data.items():
1083 if ospf_area in show_ospf_json["areas"]:
1084 if "Router Link States" in area_lsa:
1085 for lsa in area_lsa["Router Link States"]:
1086 if (
1087 lsa
1088 in show_ospf_json["areas"][ospf_area]["Router Link States"]
1089 ):
1090 logger.info(
1091 "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s",
1092 router,
1093 ospf_area,
1094 lsa,
1095 )
1096 result = True
1097 else:
1098 errormsg = (
1099 "[DUT: {}] OSPF LSDB area {}: expected"
4256a209 1100 " Router LSA is {}".format(router, ospf_area, lsa)
701a0192 1101 )
1102 return errormsg
1103 if "Net Link States" in area_lsa:
1104 for lsa in area_lsa["Net Link States"]:
1105 if lsa in show_ospf_json["areas"][ospf_area]["Net Link States"]:
1106 logger.info(
1107 "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s",
1108 router,
1109 ospf_area,
1110 lsa,
1111 )
1112 result = True
1113 else:
1114 errormsg = (
1115 "[DUT: {}] OSPF LSDB area {}: expected"
4256a209 1116 " Network LSA is {}".format(router, ospf_area, lsa)
701a0192 1117 )
1118 return errormsg
1119 if "Summary Link States" in area_lsa:
1120 for lsa in area_lsa["Summary Link States"]:
1121 if (
1122 lsa
1123 in show_ospf_json["areas"][ospf_area]["Summary Link States"]
1124 ):
1125 logger.info(
1126 "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s",
1127 router,
1128 ospf_area,
1129 lsa,
1130 )
1131 result = True
1132 else:
1133 errormsg = (
1134 "[DUT: {}] OSPF LSDB area {}: expected"
4256a209 1135 " Summary LSA is {}".format(router, ospf_area, lsa)
701a0192 1136 )
1137 return errormsg
1138 if "ASBR-Summary Link States" in area_lsa:
1139 for lsa in area_lsa["ASBR-Summary Link States"]:
1140 if (
1141 lsa
1142 in show_ospf_json["areas"][ospf_area][
1143 "ASBR-Summary Link States"
1144 ]
1145 ):
1146 logger.info(
1147 "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s",
1148 router,
1149 ospf_area,
1150 lsa,
1151 )
1152 result = True
1153 else:
1154 errormsg = (
1155 "[DUT: {}] OSPF LSDB area {}: expected"
1156 " ASBR Summary LSA is {}".format(router, ospf_area, lsa)
1157 )
1158 return errormsg
4256a209 1159 if ospf_external_lsa:
701a0192 1160 for ospf_ext_lsa, ext_lsa_data in ospf_external_lsa.items():
1161 if ospf_ext_lsa in show_ospf_json["AS External Link States"]:
1162 logger.info(
1163 "[DUT: %s] OSPF LSDB:External LSA %s", router, ospf_ext_lsa
1164 )
1165 result = True
1166 else:
1167 errormsg = (
1168 "[DUT: {}] OSPF LSDB : expected"
1169 " External LSA is {}".format(router, ospf_ext_lsa)
1170 )
1171 return errormsg
4256a209 1172
1173 logger.debug("Exiting API: verify_ospf_database()")
1174 return result
1175
1176
4256a209 1177@retry(attempts=10, wait=2, return_is_str=True)
1178def verify_ospf_summary(tgen, topo, dut, input_dict):
1179 """
1180 This API is to verify ospf routes by running
1181 show ip ospf interface command.
1182
1183 Parameters
1184 ----------
1185 * `tgen` : Topogen object
1186 * `topo` : topology descriptions
1187 * `dut`: device under test
1188 * `input_dict` : Input dict data, required when configuring from testcase
1189
1190 Usage
1191 -----
1192 input_dict = {
1193 "11.0.0.0/8": {
1194 "Summary address": "11.0.0.0/8",
1195 "Metric-type": "E2",
1196 "Metric": 20,
1197 "Tag": 0,
1198 "External route count": 5
1199 }
1200 }
1201 result = verify_ospf_summary(tgen, topo, dut, input_dict)
1202
1203 Returns
1204 -------
1205 True or False (Error Message)
1206 """
1207
1208 logger.debug("Entering lib API: verify_ospf_summary()")
1209 result = False
1210 router = dut
1211
1212 logger.info("Verifying OSPF summary on router %s:", router)
1213
701a0192 1214 if "ospf" not in topo["routers"][dut]:
1215 errormsg = "[DUT: {}] OSPF is not configured on the router.".format(router)
4256a209 1216 return errormsg
1217
1218 rnode = tgen.routers()[dut]
701a0192 1219 show_ospf_json = run_frr_cmd(rnode, "show ip ospf summary detail json", isjson=True)
4256a209 1220
1221 # Verifying output dictionary show_ospf_json is empty or not
1222 if not bool(show_ospf_json):
1223 errormsg = "OSPF is not running"
1224 return errormsg
1225
1226 # To find neighbor ip type
1227 ospf_summary_data = input_dict
1228 for ospf_summ, summ_data in ospf_summary_data.items():
1229 if ospf_summ not in show_ospf_json:
1230 continue
701a0192 1231 summary = ospf_summary_data[ospf_summ]["Summary address"]
4256a209 1232 if summary in show_ospf_json:
1233 for summ in summ_data:
1234 if summ_data[summ] == show_ospf_json[summary][summ]:
701a0192 1235 logger.info(
1236 "[DUT: %s] OSPF summary %s:%s is %s",
1237 router,
1238 summary,
1239 summ,
1240 summ_data[summ],
1241 )
4256a209 1242 result = True
1243 else:
701a0192 1244 errormsg = (
1245 "[DUT: {}] OSPF summary {}:{} is %s, "
1246 "Expected is {}".format(
1247 router, summary, summ, show_ospf_json[summary][summ]
1248 )
1249 )
4256a209 1250 return errormsg
1251
1252 logger.debug("Exiting API: verify_ospf_summary()")
1253 return result