1 # SPDX-License-Identifier: ISC
3 # Copyright (c) 2020 by VMware, Inc. ("VMware")
4 # Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
5 # ("NetDEF") in this file.
10 from copy
import deepcopy
11 from time
import sleep
13 # Import common_config to use commomnly used APIs
14 from lib
.common_config
import (
15 create_common_configurations
,
22 from lib
.topolog
import logger
23 from lib
.topotest
import frr_unicode
25 ################################
27 ################################
30 def create_router_ospf(tgen
, topo
=None, input_dict
=None, build
=False, load_config
=True):
32 API to configure ospf on router.
36 * `tgen` : Topogen object
37 * `topo` : json file data
38 * `input_dict` : Input dict data, required when configuring from testcase
39 * `build` : Only for initial setup phase this is set as True.
40 * `load_config` : Loading the config to router this is set as True.
47 "router_id": "22.22.22.22",
48 "area": [{ "id": "0.0.0.0", "type": "nssa"}]
52 result = create_router_ospf(tgen, topo, input_dict)
58 logger
.debug("Entering lib API: create_router_ospf()")
65 input_dict
= deepcopy(topo
)
67 topo
= topo
["routers"]
68 input_dict
= deepcopy(input_dict
)
70 for ospf
in ["ospf", "ospf6"]:
73 for router
in input_dict
.keys():
74 if ospf
not in input_dict
[router
]:
75 logger
.debug("Router %s: %s not present in input_dict", router
, ospf
)
78 config_data
= __create_ospf_global(
79 tgen
, input_dict
, router
, build
, load_config
, ospf
82 if router
not in config_data_dict
:
83 config_data_dict
[router
] = config_data
85 config_data_dict
[router
].extend(config_data
)
87 result
= create_common_configurations(
88 tgen
, config_data_dict
, ospf
, build
, load_config
90 except InvalidCLIError
:
91 logger
.error("create_router_ospf (ipv4)", exc_info
=True)
94 logger
.debug("Exiting lib API: create_router_ospf()")
98 def __create_ospf_global(tgen
, input_dict
, router
, build
, load_config
, ospf
):
100 Helper API to create ospf global configuration.
104 * `tgen` : Topogen object
105 * `input_dict` : Input dict data, required when configuring from testcase
106 * `router` : router to be configured.
107 * `build` : Only for initial setup phase this is set as True.
108 * `load_config` : Loading the config to router this is set as True.
109 * `ospf` : either 'ospf' or 'ospf6'
118 "ipv6": "2013:13::1/64",
122 "network": "point-to-point"
127 "router_id": "1.1.1.1",
139 list of configuration commands
144 if ospf
not in input_dict
[router
]:
147 logger
.debug("Entering lib API: __create_ospf_global()")
149 ospf_data
= input_dict
[router
][ospf
]
150 del_ospf_action
= ospf_data
.setdefault("delete", False)
152 config_data
= ["no router {}".format(ospf
)]
155 cmd
= "router {}".format(ospf
)
157 config_data
.append(cmd
)
160 router_id
= ospf_data
.setdefault("router_id", None)
161 del_router_id
= ospf_data
.setdefault("del_router_id", False)
163 config_data
.append("no {} router-id".format(ospf
))
165 config_data
.append("{} router-id {}".format(ospf
, router_id
))
167 # log-adjacency-changes
168 log_adj_changes
= ospf_data
.setdefault("log_adj_changes", None)
169 del_log_adj_changes
= ospf_data
.setdefault("del_log_adj_changes", False)
170 if del_log_adj_changes
:
171 config_data
.append("no log-adjacency-changes detail")
173 config_data
.append("log-adjacency-changes {}".format(log_adj_changes
))
176 aggr_timer
= ospf_data
.setdefault("aggr_timer", None)
177 del_aggr_timer
= ospf_data
.setdefault("del_aggr_timer", False)
179 config_data
.append("no aggregation timer")
181 config_data
.append("aggregation timer {}".format(aggr_timer
))
183 # maximum path information
184 ecmp_data
= ospf_data
.setdefault("maximum-paths", {})
186 cmd
= "maximum-paths {}".format(ecmp_data
)
187 del_action
= ospf_data
.setdefault("del_max_path", False)
189 cmd
= "no maximum-paths"
190 config_data
.append(cmd
)
193 flood_data
= ospf_data
.setdefault("flood-reduction", {})
195 cmd
= "flood-reduction"
196 del_action
= ospf_data
.setdefault("del_flood_reduction", False)
198 cmd
= "no flood-reduction"
199 config_data
.append(cmd
)
201 # LSA refresh timer - A hidden command.
202 refresh_data
= ospf_data
.setdefault("lsa-refresh", {})
204 cmd
= "ospf lsa-refresh {}".format(refresh_data
)
205 del_action
= ospf_data
.setdefault("del_lsa_refresh", False)
207 cmd
= "no ospf lsa-refresh"
208 config_data
.append(cmd
)
210 # redistribute command
211 redistribute_data
= ospf_data
.setdefault("redistribute", {})
212 if redistribute_data
:
213 for redistribute
in redistribute_data
:
214 if "redist_type" not in redistribute
:
216 "Router %s: 'redist_type' not present in " "input_dict", router
219 cmd
= "redistribute {}".format(redistribute
["redist_type"])
220 for red_type
in redistribute_data
:
221 if "route_map" in red_type
:
222 cmd
= cmd
+ " route-map {}".format(red_type
["route_map"])
223 del_action
= redistribute
.setdefault("delete", False)
225 cmd
= "no {}".format(cmd
)
226 config_data
.append(cmd
)
229 area_data
= ospf_data
.setdefault("area", {})
231 for area
in area_data
:
234 "Router %s: 'area id' not present in " "input_dict", router
237 cmd
= "area {}".format(area
["id"])
240 cmd
= cmd
+ " {}".format(area
["type"])
242 if "flood-reduction" in area
:
243 cmd
= cmd
+ " flood-reduction"
245 del_action
= area
.setdefault("delete", False)
247 cmd
= "no {}".format(cmd
)
248 config_data
.append(cmd
)
250 # def route information
251 def_rte_data
= ospf_data
.setdefault("default-information", {})
253 if "originate" not in def_rte_data
:
255 "Router %s: 'originate key' not present in " "input_dict", router
258 cmd
= "default-information originate"
260 if "always" in def_rte_data
:
261 cmd
= cmd
+ " always"
263 if "metric" in def_rte_data
:
264 cmd
= cmd
+ " metric {}".format(def_rte_data
["metric"])
266 if "metric-type" in def_rte_data
:
267 cmd
= cmd
+ " metric-type {}".format(def_rte_data
["metric-type"])
269 if "route-map" in def_rte_data
:
270 cmd
= cmd
+ " route-map {}".format(def_rte_data
["route-map"])
272 del_action
= def_rte_data
.setdefault("delete", False)
274 cmd
= "no {}".format(cmd
)
275 config_data
.append(cmd
)
277 # summary information
278 summary_data
= ospf_data
.setdefault("summary-address", {})
280 for summary
in summary_data
:
281 if "prefix" not in summary
:
283 "Router %s: 'summary-address' not present in " "input_dict",
287 cmd
= "summary {}/{}".format(summary
["prefix"], summary
["mask"])
289 _tag
= summary
.setdefault("tag", None)
291 cmd
= "{} tag {}".format(cmd
, _tag
)
293 _advertise
= summary
.setdefault("advertise", True)
295 cmd
= "{} no-advertise".format(cmd
)
297 del_action
= summary
.setdefault("delete", False)
299 cmd
= "no {}".format(cmd
)
300 config_data
.append(cmd
)
302 # ospf gr information
303 gr_data
= ospf_data
.setdefault("graceful-restart", {})
306 if "opaque" in gr_data
and gr_data
["opaque"]:
307 cmd
= "capability opaque"
308 if gr_data
.setdefault("delete", False):
309 cmd
= "no {}".format(cmd
)
310 config_data
.append(cmd
)
312 if "helper enable" in gr_data
and not gr_data
["helper enable"]:
313 cmd
= "graceful-restart helper enable"
314 if gr_data
.setdefault("delete", False):
315 cmd
= "no {}".format(cmd
)
316 config_data
.append(cmd
)
317 elif "helper enable" in gr_data
and type(gr_data
["helper enable"]) is list:
318 for rtrs
in gr_data
["helper enable"]:
319 cmd
= "graceful-restart helper enable {}".format(rtrs
)
320 if gr_data
.setdefault("delete", False):
321 cmd
= "no {}".format(cmd
)
322 config_data
.append(cmd
)
324 if "helper" in gr_data
:
325 if type(gr_data
["helper"]) is not list:
326 gr_data
["helper"] = list(gr_data
["helper"])
327 for helper_role
in gr_data
["helper"]:
328 cmd
= "graceful-restart helper {}".format(helper_role
)
329 if gr_data
.setdefault("delete", False):
330 cmd
= "no {}".format(cmd
)
331 config_data
.append(cmd
)
333 if "supported-grace-time" in gr_data
:
334 cmd
= "graceful-restart helper supported-grace-time {}".format(
335 gr_data
["supported-grace-time"]
337 if gr_data
.setdefault("delete", False):
338 cmd
= "no {}".format(cmd
)
339 config_data
.append(cmd
)
341 logger
.debug("Exiting lib API: create_ospf_global()")
346 def config_ospf_interface(
347 tgen
, topo
=None, input_dict
=None, build
=False, load_config
=True
350 API to configure ospf on router.
354 * `tgen` : Topogen object
355 * `topo` : json file data
356 * `input_dict` : Input dict data, required when configuring from testcase
357 * `build` : Only for initial setup phase this is set as True.
358 * `load_config` : Loading the config to router this is set as True.
367 "authentication": "message-digest",
368 "authentication-key": "ospf",
369 "message-digest-key": "10"
375 result = config_ospf_interface(tgen, topo, r1_ospf_auth)
381 logger
.debug("Enter lib config_ospf_interface")
385 topo
= tgen
.json_topo
388 input_dict
= deepcopy(topo
)
390 input_dict
= deepcopy(input_dict
)
392 config_data_dict
= {}
394 for router
in input_dict
.keys():
396 for lnk
in input_dict
[router
]["links"].keys():
397 if "ospf" not in input_dict
[router
]["links"][lnk
]:
399 "Router %s: ospf config is not present in" "input_dict", router
402 ospf_data
= input_dict
[router
]["links"][lnk
]["ospf"]
403 data_ospf_area
= ospf_data
.setdefault("area", None)
404 data_ospf_auth
= ospf_data
.setdefault("authentication", None)
405 data_ospf_dr_priority
= ospf_data
.setdefault("priority", None)
406 data_ospf_cost
= ospf_data
.setdefault("cost", None)
407 data_ospf_mtu
= ospf_data
.setdefault("mtu_ignore", None)
410 intf
= topo
["routers"][router
]["links"][lnk
]["interface"]
412 intf
= topo
["switches"][router
]["links"][lnk
]["interface"]
415 cmd
= "interface {}".format(intf
)
417 config_data
.append(cmd
)
418 # interface area config
420 cmd
= "ip ospf area {}".format(data_ospf_area
)
421 config_data
.append(cmd
)
423 # interface ospf auth
425 if data_ospf_auth
== "null":
426 cmd
= "ip ospf authentication null"
427 elif data_ospf_auth
== "message-digest":
428 cmd
= "ip ospf authentication message-digest"
430 cmd
= "ip ospf authentication"
432 if "del_action" in ospf_data
:
433 cmd
= "no {}".format(cmd
)
434 config_data
.append(cmd
)
436 if "message-digest-key" in ospf_data
:
437 cmd
= "ip ospf message-digest-key {} md5 {}".format(
438 ospf_data
["message-digest-key"], ospf_data
["authentication-key"]
440 if "del_action" in ospf_data
:
441 cmd
= "no {}".format(cmd
)
442 config_data
.append(cmd
)
445 "authentication-key" in ospf_data
446 and "message-digest-key" not in ospf_data
448 cmd
= "ip ospf authentication-key {}".format(
449 ospf_data
["authentication-key"]
451 if "del_action" in ospf_data
:
452 cmd
= "no {}".format(cmd
)
453 config_data
.append(cmd
)
455 # interface ospf dr priority
456 if data_ospf_dr_priority
:
457 cmd
= "ip ospf priority {}".format(ospf_data
["priority"])
458 if "del_action" in ospf_data
:
459 cmd
= "no {}".format(cmd
)
460 config_data
.append(cmd
)
462 # interface ospf cost
464 cmd
= "ip ospf cost {}".format(ospf_data
["cost"])
465 if "del_action" in ospf_data
:
466 cmd
= "no {}".format(cmd
)
467 config_data
.append(cmd
)
471 cmd
= "ip ospf mtu-ignore"
472 if "del_action" in ospf_data
:
473 cmd
= "no {}".format(cmd
)
474 config_data
.append(cmd
)
480 config_data_dict
[router
] = config_data
482 result
= create_common_configurations(
483 tgen
, config_data_dict
, "interface_config", build
=build
486 logger
.debug("Exiting lib API: config_ospf_interface()")
490 def clear_ospf(tgen
, router
, ospf
=None):
492 This API is to clear ospf neighborship by running
493 clear ip ospf interface * command,
497 * `tgen`: topogen object
498 * `router`: device under test
502 clear_ospf(tgen, "r1")
505 logger
.debug("Entering lib API: clear_ospf()")
506 if router
not in tgen
.routers():
509 rnode
= tgen
.routers()[router
]
516 cmd
= "clear {} ospf interface".format(version
)
517 logger
.info("Clearing ospf process on router %s.. using command '%s'", router
, cmd
)
518 run_frr_cmd(rnode
, cmd
)
520 logger
.debug("Exiting lib API: clear_ospf()")
523 def redistribute_ospf(tgen
, topo
, dut
, route_type
, **kwargs
):
525 Redstribution of routes inside ospf.
529 * `tgen`: Topogen object
530 * `topo` : json file data
531 * `dut`: device under test
532 * `route_type`: "static" or "connected" or ....
533 * `kwargs`: pass extra information (see below)
537 redistribute_ospf(tgen, topo, "r0", "static", delete=True)
538 redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
541 ospf_red
= {dut
: {"ospf": {"redistribute": [{"redist_type": route_type
}]}}}
542 for k
, v
in kwargs
.items():
543 ospf_red
[dut
]["ospf"]["redistribute"][0][k
] = v
545 result
= create_router_ospf(tgen
, topo
, ospf_red
)
546 assert result
is True, "Testcase : Failed \n Error: {}".format(result
)
549 ################################
551 ################################
552 @retry(retry_timeout
=80)
553 def verify_ospf_neighbor(
554 tgen
, topo
=None, dut
=None, input_dict
=None, lan
=False, expected
=True
557 This API is to verify ospf neighborship by running
558 show ip ospf neighbour command,
562 * `tgen` : Topogen object
563 * `topo` : json file data
564 * `dut`: device under test
565 * `input_dict` : Input dict data, required when configuring from testcase
566 * `lan` : verify neighbors in lan topology
567 * `expected` : expected results from API, by-default True
571 1. To check FULL neighbors.
572 verify_ospf_neighbor(tgen, topo, dut=dut)
574 2. To check neighbors with their roles.
595 result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
599 True or False (Error Message)
601 logger
.debug("Entering lib API: verify_ospf_neighbor()")
604 topo
= tgen
.json_topo
607 for router
, rnode
in tgen
.routers().items():
608 if "ospf" not in topo
["routers"][router
]:
611 if dut
is not None and dut
!= router
:
614 logger
.info("Verifying OSPF neighborship on router %s:", router
)
615 show_ospf_json
= run_frr_cmd(
616 rnode
, "show ip ospf neighbor all json", isjson
=True
619 # Verifying output dictionary show_ospf_json is empty or not
620 if not bool(show_ospf_json
):
621 errormsg
= "OSPF is not running"
624 ospf_data_list
= input_dict
[router
]["ospf"]
625 ospf_nbr_list
= ospf_data_list
["neighbors"]
627 for ospf_nbr
, nbr_data
in ospf_nbr_list
.items():
628 data_ip
= topo
["routers"][ospf_nbr
]["links"]
629 data_rid
= topo
["routers"][ospf_nbr
]["ospf"]["router_id"]
630 if ospf_nbr
in data_ip
:
631 nbr_details
= nbr_data
[ospf_nbr
]
633 for switch
in topo
["switches"]:
634 if "ospf" in topo
["switches"][switch
]["links"][router
]:
635 neighbor_ip
= data_ip
[switch
]["ipv4"].split("/")[0]
639 neighbor_ip
= data_ip
[router
]["ipv4"].split("/")[0]
642 neighbor_ip
= neighbor_ip
.lower()
645 nh_state
= show_ospf_json
[nbr_rid
][0]["state"].split("/")[0]
646 intf_state
= show_ospf_json
[nbr_rid
][0]["state"].split("/")[1]
648 errormsg
= "[DUT: {}] OSPF peer {} missing".format(router
, nbr_rid
)
651 nbr_state
= nbr_data
.setdefault("state", None)
652 nbr_role
= nbr_data
.setdefault("role", None)
655 if nbr_state
== nh_state
:
657 "[DUT: {}] OSPF Nbr is {}:{} State {}".format(
658 router
, ospf_nbr
, nbr_rid
, nh_state
664 "[DUT: {}] OSPF is not Converged, neighbor"
665 " state is {}".format(router
, nh_state
)
669 if nbr_role
== intf_state
:
671 "[DUT: {}] OSPF Nbr is {}: {} Role {}".format(
672 router
, ospf_nbr
, nbr_rid
, nbr_role
677 "[DUT: {}] OSPF is not Converged with rid"
678 "{}, role is {}".format(router
, nbr_rid
, intf_state
)
683 for router
, rnode
in tgen
.routers().items():
684 if "ospf" not in topo
["routers"][router
]:
687 if dut
is not None and dut
!= router
:
690 logger
.info("Verifying OSPF neighborship on router %s:", router
)
691 show_ospf_json
= run_frr_cmd(
692 rnode
, "show ip ospf neighbor all json", isjson
=True
694 # Verifying output dictionary show_ospf_json is empty or not
695 if not bool(show_ospf_json
):
696 errormsg
= "OSPF is not running"
699 ospf_data_list
= topo
["routers"][router
]["ospf"]
700 ospf_neighbors
= ospf_data_list
["neighbors"]
702 total_peer
= len(ospf_neighbors
.keys())
704 ospf_nbr_list
= ospf_data_list
["neighbors"]
706 for ospf_nbr
, nbr_data
in ospf_nbr_list
.items():
708 data_ip
= topo
["routers"][nbr_data
["nbr"]]["links"]
709 data_rid
= topo
["routers"][nbr_data
["nbr"]]["ospf"]["router_id"]
711 data_ip
= topo
["routers"][ospf_nbr
]["links"]
712 data_rid
= topo
["routers"][ospf_nbr
]["ospf"]["router_id"]
713 if ospf_nbr
in data_ip
:
714 nbr_details
= nbr_data
[ospf_nbr
]
716 for switch
in topo
["switches"]:
717 if "ospf" in topo
["switches"][switch
]["links"][router
]:
718 neighbor_ip
= data_ip
[switch
]["ipv4"].split("/")[0]
722 neighbor_ip
= data_ip
[router
]["ipv4"].split("/")[0]
725 neighbor_ip
= neighbor_ip
.lower()
728 nh_state
= show_ospf_json
[nbr_rid
][0]["state"].split("/")[0]
730 errormsg
= "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
731 router
, nbr_rid
, ospf_nbr
735 if nh_state
== "Full":
738 if no_of_peer
== total_peer
:
739 logger
.info("[DUT: {}] OSPF is Converged".format(router
))
742 errormsg
= "[DUT: {}] OSPF is not Converged".format(router
)
745 logger
.debug("Exiting API: verify_ospf_neighbor()")
749 @retry(retry_timeout
=50)
750 def verify_ospf6_neighbor(tgen
, topo
=None, dut
=None, input_dict
=None, lan
=False):
752 This API is to verify ospf neighborship by running
753 show ipv6 ospf neighbour command,
757 * `tgen` : Topogen object
758 * `topo` : json file data
759 * `dut`: device under test
760 * `input_dict` : Input dict data, required when configuring from testcase
761 * `lan` : verify neighbors in lan topology
765 1. To check FULL neighbors.
766 verify_ospf_neighbor(tgen, topo, dut=dut)
768 2. To check neighbors with their roles.
789 result = verify_ospf6_neighbor(tgen, topo, dut, input_dict, lan=True)
791 3. To check there are no neighbors.
799 result = verify_ospf6_neighbor(tgen, topo, dut, input_dict)
803 True or False (Error Message)
805 logger
.debug("Entering lib API: {}".format(sys
._getframe
().f_code
.co_name
))
809 topo
= tgen
.json_topo
812 for router
, rnode
in tgen
.routers().items():
813 if "ospf6" not in topo
["routers"][router
]:
816 if dut
is not None and dut
!= router
:
819 logger
.info("Verifying OSPF neighborship on router %s:", router
)
820 show_ospf_json
= run_frr_cmd(
821 rnode
, "show ipv6 ospf neighbor json", isjson
=True
823 # Verifying output dictionary show_ospf_json is empty or not
824 if not bool(show_ospf_json
):
825 errormsg
= "OSPF6 is not running"
828 ospf_data_list
= input_dict
[router
]["ospf6"]
829 ospf_nbr_list
= ospf_data_list
["neighbors"]
831 # Check if looking for no neighbors
832 if ospf_nbr_list
== []:
833 if show_ospf_json
["neighbors"] == []:
834 logger
.info("[DUT: {}] OSPF6 no neighbors found".format(router
))
838 "[DUT: {}] OSPF6 active neighbors found, expected None".format(
844 for ospf_nbr
, nbr_data
in ospf_nbr_list
.items():
847 data_ip
= data_rid
= topo
["routers"][ospf_nbr
]["ospf6"]["router_id"]
849 data_ip
= data_rid
= topo
["routers"][nbr_data
["nbr"]]["ospf6"][
853 if ospf_nbr
in data_ip
:
854 nbr_details
= nbr_data
[ospf_nbr
]
856 for switch
in topo
["switches"]:
857 if "ospf6" in topo
["switches"][switch
]["links"][router
]:
858 neighbor_ip
= data_ip
862 neighbor_ip
= data_ip
[router
]["ipv6"].split("/")[0]
865 neighbor_ip
= neighbor_ip
.lower()
867 get_index_val
= dict(
868 (d
["neighborId"], dict(d
, index
=index
))
869 for (index
, d
) in enumerate(show_ospf_json
["neighbors"])
872 nh_state
= get_index_val
.get(neighbor_ip
)["state"]
873 intf_state
= get_index_val
.get(neighbor_ip
)["ifState"]
875 errormsg
= "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
876 router
, nbr_rid
, ospf_nbr
880 nbr_state
= nbr_data
.setdefault("state", None)
881 nbr_role
= nbr_data
.setdefault("role", None)
884 if nbr_state
== nh_state
:
886 "[DUT: {}] OSPF6 Nbr is {}:{} State {}".format(
887 router
, ospf_nbr
, nbr_rid
, nh_state
893 "[DUT: {}] OSPF6 is not Converged, neighbor"
894 " state is {} , Expected state is {}".format(
895 router
, nh_state
, nbr_state
900 if nbr_role
== intf_state
:
902 "[DUT: {}] OSPF6 Nbr is {}: {} Role {}".format(
903 router
, ospf_nbr
, nbr_rid
, nbr_role
908 "[DUT: {}] OSPF6 is not Converged with rid"
909 "{}, role is {}, Expected role is {}".format(
910 router
, nbr_rid
, intf_state
, nbr_role
917 for router
, rnode
in tgen
.routers().items():
918 if "ospf6" not in topo
["routers"][router
]:
921 if dut
is not None and dut
!= router
:
924 logger
.info("Verifying OSPF6 neighborship on router %s:", router
)
925 show_ospf_json
= run_frr_cmd(
926 rnode
, "show ipv6 ospf neighbor json", isjson
=True
928 # Verifying output dictionary show_ospf_json is empty or not
929 if not bool(show_ospf_json
):
930 errormsg
= "OSPF6 is not running"
933 ospf_data_list
= topo
["routers"][router
]["ospf6"]
934 ospf_neighbors
= ospf_data_list
["neighbors"]
936 total_peer
= len(ospf_neighbors
.keys())
938 ospf_nbr_list
= ospf_data_list
["neighbors"]
940 for ospf_nbr
, nbr_data
in ospf_nbr_list
.items():
942 data_ip
= data_rid
= topo
["routers"][ospf_nbr
]["ospf6"]["router_id"]
944 data_ip
= data_rid
= topo
["routers"][nbr_data
["nbr"]]["ospf6"][
948 if ospf_nbr
in data_ip
:
949 nbr_details
= nbr_data
[ospf_nbr
]
951 for switch
in topo
["switches"]:
952 if "ospf6" in topo
["switches"][switch
]["links"][router
]:
953 neighbor_ip
= data_ip
957 neighbor_ip
= data_ip
960 neighbor_ip
= neighbor_ip
.lower()
962 get_index_val
= dict(
963 (d
["neighborId"], dict(d
, index
=index
))
964 for (index
, d
) in enumerate(show_ospf_json
["neighbors"])
967 nh_state
= get_index_val
.get(neighbor_ip
)["state"]
968 intf_state
= get_index_val
.get(neighbor_ip
)["ifState"]
970 errormsg
= "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
971 router
, nbr_rid
, ospf_nbr
975 if nh_state
== "Full":
978 if no_of_peer
== total_peer
:
979 logger
.info("[DUT: {}] OSPF6 is Converged".format(router
))
982 errormsg
= "[DUT: {}] OSPF6 is not Converged".format(router
)
985 logger
.debug("Exiting lib API: {}".format(sys
._getframe
().f_code
.co_name
))
989 @retry(retry_timeout
=40)
991 tgen
, dut
, input_dict
, next_hop
=None, tag
=None, metric
=None, fib
=None, expected
=True
994 This API is to verify ospf routes by running
995 show ip ospf route command.
999 * `tgen` : Topogen object
1000 * `dut`: device under test
1001 * `input_dict` : Input dict data, required when configuring from testcase
1002 * `next_hop` : next to be verified
1003 * `tag` : tag to be verified
1004 * `metric` : metric to be verified
1005 * `fib` : True if the route is installed in FIB.
1006 * `expected` : expected results from API, by-default True
1022 result = verify_ospf_rib(tgen, dut, input_dict,next_hop=nh)
1026 True or False (Error Message)
1029 logger
.info("Entering lib API: verify_ospf_rib()")
1031 router_list
= tgen
.routers()
1032 additional_nexthops_in_required_nhs
= []
1034 for routerInput
in input_dict
.keys():
1035 for router
, rnode
in router_list
.items():
1039 logger
.info("Checking router %s RIB:", router
)
1041 # Verifying RIB routes
1042 command
= "show ip ospf route"
1048 "static_routes" in input_dict
[routerInput
]
1049 or "prefix" in input_dict
[routerInput
]
1051 if "prefix" in input_dict
[routerInput
]:
1052 static_routes
= input_dict
[routerInput
]["prefix"]
1054 static_routes
= input_dict
[routerInput
]["static_routes"]
1056 for static_route
in static_routes
:
1057 cmd
= "{}".format(command
)
1059 cmd
= "{} json".format(cmd
)
1061 ospf_rib_json
= run_frr_cmd(rnode
, cmd
, isjson
=True)
1063 # Verifying output dictionary ospf_rib_json is not empty
1064 if bool(ospf_rib_json
) is False:
1066 "[DUT: {}] No routes found in OSPF route "
1067 "table".format(router
)
1071 network
= static_route
["network"]
1072 no_of_ip
= static_route
.setdefault("no_of_ip", 1)
1073 _tag
= static_route
.setdefault("tag", None)
1074 _rtype
= static_route
.setdefault("routeType", None)
1076 # Generating IPs for verification
1077 ip_list
= generate_ips(network
, no_of_ip
)
1081 for st_rt
in ip_list
:
1082 st_rt
= str(ipaddress
.ip_network(frr_unicode(st_rt
)))
1084 _addr_type
= validate_ip_address(st_rt
)
1085 if _addr_type
!= "ipv4":
1088 if st_rt
in ospf_rib_json
:
1090 found_routes
.append(st_rt
)
1092 if fib
and next_hop
:
1093 if type(next_hop
) is not list:
1094 next_hop
= [next_hop
]
1096 for mnh
in range(0, len(ospf_rib_json
[st_rt
])):
1099 in ospf_rib_json
[st_rt
][mnh
]["nexthops"][0]
1104 for rib_r
in ospf_rib_json
[st_rt
][mnh
][
1111 missing_list_of_nexthops
= set(
1113 ).difference(next_hop
)
1114 additional_nexthops_in_required_nhs
= set(
1116 ).difference(found_hops
[0])
1118 if additional_nexthops_in_required_nhs
:
1121 "%s is not active for route %s in "
1122 "RIB of router %s\n",
1123 additional_nexthops_in_required_nhs
,
1128 "Nexthop {} is not active"
1129 " for route {} in RIB of router"
1131 additional_nexthops_in_required_nhs
,
1140 elif next_hop
and fib
is None:
1141 if type(next_hop
) is not list:
1142 next_hop
= [next_hop
]
1145 for rib_r
in ospf_rib_json
[st_rt
]["nexthops"]
1149 missing_list_of_nexthops
= set(
1151 ).difference(next_hop
)
1152 additional_nexthops_in_required_nhs
= set(
1154 ).difference(found_hops
)
1156 if additional_nexthops_in_required_nhs
:
1158 "Missing nexthop %s for route"
1159 " %s in RIB of router %s\n",
1160 additional_nexthops_in_required_nhs
,
1165 "Nexthop {} is Missing for "
1166 "route {} in RIB of router {}\n".format(
1167 additional_nexthops_in_required_nhs
,
1176 if "routeType" not in ospf_rib_json
[st_rt
]:
1178 "[DUT: {}]: routeType missing"
1179 " for route {} in OSPF RIB \n".format(
1184 elif _rtype
!= ospf_rib_json
[st_rt
]["routeType"]:
1186 "[DUT: {}]: routeType mismatch"
1187 " for route {} in OSPF RIB \n".format(
1194 "[DUT: {}]: Found routeType {}"
1195 " for route {}".format(dut
, _rtype
, st_rt
)
1198 if "tag" not in ospf_rib_json
[st_rt
]:
1200 "[DUT: {}]: tag is not"
1202 " route {} in RIB \n".format(dut
, st_rt
)
1206 if _tag
!= ospf_rib_json
[st_rt
]["tag"]:
1208 "[DUT: {}]: tag value {}"
1209 " is not matched for"
1210 " route {} in RIB \n".format(
1218 if metric
is not None:
1219 if "type2cost" not in ospf_rib_json
[st_rt
]:
1221 "[DUT: {}]: metric is"
1223 " route {} in RIB \n".format(dut
, st_rt
)
1227 if metric
!= ospf_rib_json
[st_rt
]["type2cost"]:
1229 "[DUT: {}]: metric value "
1230 "{} is not matched for "
1231 "route {} in RIB \n".format(
1240 missing_routes
.append(st_rt
)
1244 "[DUT: {}]: Found next_hop {} for all OSPF"
1245 " routes in RIB".format(router
, next_hop
)
1248 if len(missing_routes
) > 0:
1249 errormsg
= "[DUT: {}]: Missing route in RIB, " "routes: {}".format(
1256 "[DUT: %s]: Verified routes in RIB, found" " routes are: %s\n",
1262 logger
.info("Exiting lib API: verify_ospf_rib()")
1266 @retry(retry_timeout
=20)
1267 def verify_ospf_interface(
1268 tgen
, topo
=None, dut
=None, lan
=False, input_dict
=None, expected
=True
1271 This API is to verify ospf routes by running
1272 show ip ospf interface command.
1276 * `tgen` : Topogen object
1277 * `topo` : topology descriptions
1278 * `dut`: device under test
1279 * `lan`: if set to true this interface belongs to LAN.
1280 * `input_dict` : Input dict data, required when configuring from testcase
1281 * `expected` : expected results from API, by-default True
1293 'mcastMemberOspfDesignatedRouters': True,
1294 'mcastMemberOspfAllRouters': True,
1295 'ospfEnabled': True,
1302 result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
1306 True or False (Error Message)
1309 logger
.debug("Entering lib API: verify_ospf_interface()")
1312 topo
= tgen
.json_topo
1314 for router
, rnode
in tgen
.routers().items():
1315 if "ospf" not in topo
["routers"][router
]:
1318 if dut
is not None and dut
!= router
:
1321 logger
.info("Verifying OSPF interface on router %s:", router
)
1322 show_ospf_json
= run_frr_cmd(rnode
, "show ip ospf interface json", isjson
=True)
1324 # Verifying output dictionary show_ospf_json is empty or not
1325 if not bool(show_ospf_json
):
1326 errormsg
= "OSPF is not running"
1329 # To find neighbor ip type
1330 ospf_intf_data
= input_dict
[router
]["links"]
1331 for ospf_intf
, intf_data
in ospf_intf_data
.items():
1332 intf
= topo
["routers"][router
]["links"][ospf_intf
]["interface"]
1333 if intf
in show_ospf_json
["interfaces"]:
1334 for intf_attribute
in intf_data
["ospf"]:
1336 intf_data
["ospf"][intf_attribute
]
1337 == show_ospf_json
["interfaces"][intf
][intf_attribute
]
1340 "[DUT: %s] OSPF interface %s: %s is %s",
1344 intf_data
["ospf"][intf_attribute
],
1347 errormsg
= "[DUT: {}] OSPF interface {}: {} is {}, \
1348 Expected is {}".format(
1352 intf_data
["ospf"][intf_attribute
],
1353 show_ospf_json
["interfaces"][intf
][intf_attribute
],
1357 logger
.debug("Exiting API: verify_ospf_interface()")
1361 @retry(retry_timeout
=40)
1362 def verify_ospf_database(
1363 tgen
, topo
, dut
, input_dict
, vrf
=None, lsatype
=None, rid
=None, expected
=True
1366 This API is to verify ospf lsa's by running
1367 show ip ospf database command.
1371 * `tgen` : Topogen object
1372 * `dut`: device under test
1373 * `input_dict` : Input dict data, required when configuring from testcase
1374 * `topo` : next to be verified
1375 * `expected` : expected results from API, by-default True
1382 "Router Link States": {
1383 "100.1.1.0-100.1.1.0": {
1384 "LSID": "100.1.1.0",
1385 "Advertised router": "100.1.1.0",
1387 "Sequence Number": "80000006",
1392 "Net Link States": {
1393 "10.0.0.2-100.1.1.1": {
1395 "Advertised router": "100.1.1.1",
1397 "Sequence Number": "80000001",
1404 result = verify_ospf_database(tgen, topo, dut, input_dict)
1408 True or False (Error Message)
1413 logger
.debug("Entering lib API: verify_ospf_database()")
1415 if "ospf" not in topo
["routers"][dut
]:
1416 errormsg
= "[DUT: {}] OSPF is not configured on the router.".format(dut
)
1419 rnode
= tgen
.routers()[dut
]
1421 logger
.info("Verifying OSPF interface on router %s:", dut
)
1424 rid
= "self-originate"
1427 command
= "show ip ospf database {} {} json".format(lsatype
, rid
)
1429 command
= "show ip ospf database {} {} vrf {} json".format(
1434 command
= "show ip ospf database json"
1436 command
= "show ip ospf database vrf {} json".format(vrf
)
1438 show_ospf_json
= run_frr_cmd(rnode
, command
, isjson
=True)
1439 # Verifying output dictionary show_ospf_json is empty or not
1440 if not bool(show_ospf_json
):
1441 errormsg
= "OSPF is not running"
1444 # for inter and inter lsa's
1445 ospf_db_data
= input_dict
.setdefault("areas", None)
1446 ospf_external_lsa
= input_dict
.setdefault("AS External Link States", None)
1447 # import pdb; pdb.set_trace()
1449 for ospf_area
, area_lsa
in ospf_db_data
.items():
1450 if ospf_area
in show_ospf_json
["routerLinkStates"]["areas"]:
1451 if "routerLinkStates" in area_lsa
:
1452 for lsa
in area_lsa
["routerLinkStates"]:
1453 _advrtr
= lsa
.setdefault("advertisedRouter", None)
1454 _options
= lsa
.setdefault("options", None)
1459 == show_ospf_json
["routerLinkStates"]["areas"][ospf_area
][
1463 == show_ospf_json
["routerLinkStates"]["areas"][ospf_area
][
1470 errormsg
= '[DUT: {}] OSPF LSA options: expected {}, Received Options are {} lsa["options"] {} OSPF LSAID: expected lsaid {}, Received lsaid {}'.format(
1472 show_ospf_json
["routerLinkStates"]["areas"][ospf_area
][
1477 show_ospf_json
["routerLinkStates"]["areas"][ospf_area
][
1483 if "Net Link States" in area_lsa
:
1484 for lsa
in area_lsa
["Net Link States"]:
1485 if lsa
in show_ospf_json
["areas"][ospf_area
]["Net Link States"]:
1487 "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s",
1495 "[DUT: {}] OSPF LSDB area {}: expected"
1496 " Network LSA is {}".format(router
, ospf_area
, lsa
)
1499 if "Summary Link States" in area_lsa
:
1500 for lsa
in area_lsa
["Summary Link States"]:
1503 in show_ospf_json
["areas"][ospf_area
]["Summary Link States"]
1506 "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s",
1514 "[DUT: {}] OSPF LSDB area {}: expected"
1515 " Summary LSA is {}".format(router
, ospf_area
, lsa
)
1518 if "ASBR-Summary Link States" in area_lsa
:
1519 for lsa
in area_lsa
["ASBR-Summary Link States"]:
1522 in show_ospf_json
["areas"][ospf_area
][
1523 "ASBR-Summary Link States"
1527 "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s",
1535 "[DUT: {}] OSPF LSDB area {}: expected"
1536 " ASBR Summary LSA is {}".format(router
, ospf_area
, lsa
)
1539 if ospf_external_lsa
:
1540 for ospf_ext_lsa
, ext_lsa_data
in ospf_external_lsa
.items():
1541 if ospf_ext_lsa
in show_ospf_json
["AS External Link States"]:
1543 "[DUT: %s] OSPF LSDB:External LSA %s", router
, ospf_ext_lsa
1548 "[DUT: {}] OSPF LSDB : expected"
1549 " External LSA is {}".format(router
, ospf_ext_lsa
)
1553 logger
.debug("Exiting API: verify_ospf_database()")
1557 @retry(retry_timeout
=20)
1558 def verify_ospf_summary(tgen
, topo
, dut
, input_dict
, ospf
=None, expected
=True):
1560 This API is to verify ospf routes by running
1561 show ip ospf interface command.
1565 * `tgen` : Topogen object
1566 * `topo` : topology descriptions
1567 * `dut`: device under test
1568 * `input_dict` : Input dict data, required when configuring from testcase
1574 "summaryAddress": "11.0.0.0/8",
1578 "externalRouteCount": 5
1581 result = verify_ospf_summary(tgen, topo, dut, input_dict)
1585 True or False (Error Message)
1588 logger
.debug("Entering lib API: {}".format(sys
._getframe
().f_code
.co_name
))
1592 logger
.info("Verifying OSPF summary on router %s:", router
)
1594 rnode
= tgen
.routers()[dut
]
1597 if "ospf6" not in topo
["routers"][dut
]:
1598 errormsg
= "[DUT: {}] OSPF6 is not configured on the router.".format(router
)
1601 show_ospf_json
= run_frr_cmd(
1602 rnode
, "show ipv6 ospf summary detail json", isjson
=True
1605 if "ospf" not in topo
["routers"][dut
]:
1606 errormsg
= "[DUT: {}] OSPF is not configured on the router.".format(router
)
1609 show_ospf_json
= run_frr_cmd(
1610 rnode
, "show ip ospf summary detail json", isjson
=True
1613 # Verifying output dictionary show_ospf_json is empty or not
1614 if not bool(show_ospf_json
):
1615 errormsg
= "OSPF is not running"
1618 # To find neighbor ip type
1619 ospf_summary_data
= input_dict
1622 show_ospf_json
= show_ospf_json
["default"]
1624 for ospf_summ
, summ_data
in ospf_summary_data
.items():
1625 if ospf_summ
not in show_ospf_json
:
1627 summary
= ospf_summary_data
[ospf_summ
]["summaryAddress"]
1629 if summary
in show_ospf_json
:
1630 for summ
in summ_data
:
1631 if summ_data
[summ
] == show_ospf_json
[summary
][summ
]:
1633 "[DUT: %s] OSPF summary %s:%s is %s",
1642 "[DUT: {}] OSPF summary {} : {} is {}, "
1643 "Expected is {}".format(
1647 show_ospf_json
[summary
][summ
],
1653 logger
.debug("Exiting lib API: {}".format(sys
._getframe
().f_code
.co_name
))
1657 @retry(retry_timeout
=30)
1658 def verify_ospf6_rib(
1659 tgen
, dut
, input_dict
, next_hop
=None, tag
=None, metric
=None, fib
=None
1662 This API is to verify ospf routes by running
1663 show ip ospf route command.
1667 * `tgen` : Topogen object
1668 * `dut`: device under test
1669 * `input_dict` : Input dict data, required when configuring from testcase
1670 * `next_hop` : next to be verified
1671 * `tag` : tag to be verified
1672 * `metric` : metric to be verified
1673 * `fib` : True if the route is installed in FIB.
1689 result = verify_ospf6_rib(tgen, dut, input_dict,next_hop=nh)
1693 True or False (Error Message)
1696 logger
.debug("Entering lib API: {}".format(sys
._getframe
().f_code
.co_name
))
1698 router_list
= tgen
.routers()
1699 additional_nexthops_in_required_nhs
= []
1701 for routerInput
in input_dict
.keys():
1702 for router
, rnode
in router_list
.items():
1706 logger
.info("Checking router %s RIB:", router
)
1708 # Verifying RIB routes
1709 command
= "show ipv6 ospf route detail"
1715 "static_routes" in input_dict
[routerInput
]
1716 or "prefix" in input_dict
[routerInput
]
1718 if "prefix" in input_dict
[routerInput
]:
1719 static_routes
= input_dict
[routerInput
]["prefix"]
1721 static_routes
= input_dict
[routerInput
]["static_routes"]
1723 for static_route
in static_routes
:
1724 cmd
= "{}".format(command
)
1726 cmd
= "{} json".format(cmd
)
1728 ospf_rib_json
= run_frr_cmd(rnode
, cmd
, isjson
=True)
1730 # Fix for PR 2644182
1732 ospf_rib_json
= ospf_rib_json
["routes"]
1736 # Verifying output dictionary ospf_rib_json is not empty
1737 if bool(ospf_rib_json
) is False:
1739 "[DUT: {}] No routes found in OSPF6 route "
1740 "table".format(router
)
1744 network
= static_route
["network"]
1745 no_of_ip
= static_route
.setdefault("no_of_ip", 1)
1746 _tag
= static_route
.setdefault("tag", None)
1747 _rtype
= static_route
.setdefault("routeType", None)
1749 # Generating IPs for verification
1750 ip_list
= generate_ips(network
, no_of_ip
)
1751 if len(ip_list
) == 1:
1755 for st_rt
in ip_list
:
1756 st_rt
= str(ipaddress
.ip_network(frr_unicode(st_rt
)))
1758 _addr_type
= validate_ip_address(st_rt
)
1759 if _addr_type
!= "ipv6":
1762 if st_rt
in ospf_rib_json
:
1765 found_routes
.append(st_rt
)
1767 if fib
and next_hop
:
1768 if type(next_hop
) is not list:
1769 next_hop
= [next_hop
]
1771 for mnh
in range(0, len(ospf_rib_json
[st_rt
])):
1774 in ospf_rib_json
[st_rt
][mnh
]["nextHops"][0]
1779 for rib_r
in ospf_rib_json
[st_rt
][mnh
][
1786 missing_list_of_nexthops
= set(
1788 ).difference(next_hop
)
1789 additional_nexthops_in_required_nhs
= set(
1791 ).difference(found_hops
[0])
1793 if additional_nexthops_in_required_nhs
:
1796 "%s is not active for route %s in "
1797 "RIB of router %s\n",
1798 additional_nexthops_in_required_nhs
,
1803 "Nexthop {} is not active"
1804 " for route {} in RIB of router"
1806 additional_nexthops_in_required_nhs
,
1815 elif next_hop
and fib
is None:
1816 if type(next_hop
) is not list:
1817 next_hop
= [next_hop
]
1820 for rib_r
in ospf_rib_json
[st_rt
]["nextHops"]
1824 missing_list_of_nexthops
= set(
1826 ).difference(next_hop
)
1827 additional_nexthops_in_required_nhs
= set(
1829 ).difference(found_hops
)
1830 if additional_nexthops_in_required_nhs
:
1832 "Missing nexthop %s for route"
1833 " %s in RIB of router %s\n",
1834 additional_nexthops_in_required_nhs
,
1839 "Nexthop {} is Missing for "
1840 "route {} in RIB of router {}\n".format(
1841 additional_nexthops_in_required_nhs
,
1850 if "destinationType" not in ospf_rib_json
[st_rt
]:
1852 "[DUT: {}]: destinationType missing"
1853 "for route {} in OSPF RIB \n".format(dut
, st_rt
)
1856 elif _rtype
!= ospf_rib_json
[st_rt
]["destinationType"]:
1858 "[DUT: {}]: destinationType mismatch"
1859 "for route {} in OSPF RIB \n".format(dut
, st_rt
)
1864 "DUT: {}]: Found destinationType {}"
1865 "for route {}".format(dut
, _rtype
, st_rt
)
1868 if "tag" not in ospf_rib_json
[st_rt
]:
1870 "[DUT: {}]: tag is not"
1872 " route {} in RIB \n".format(dut
, st_rt
)
1876 if _tag
!= ospf_rib_json
[st_rt
]["tag"]:
1878 "[DUT: {}]: tag value {}"
1879 " is not matched for"
1880 " route {} in RIB \n".format(
1888 if metric
is not None:
1889 if "metricCostE2" not in ospf_rib_json
[st_rt
]:
1891 "[DUT: {}]: metric is"
1893 " route {} in RIB \n".format(dut
, st_rt
)
1897 if metric
!= ospf_rib_json
[st_rt
]["metricCostE2"]:
1899 "[DUT: {}]: metric value "
1900 "{} is not matched for "
1901 "route {} in RIB \n".format(
1910 missing_routes
.append(st_rt
)
1914 "[DUT: {}]: Found next_hop {} for all OSPF"
1915 " routes in RIB".format(router
, next_hop
)
1918 if len(missing_routes
) > 0:
1919 errormsg
= "[DUT: {}]: Missing route in RIB, " "routes: {}".format(
1926 "[DUT: %s]: Verified routes in RIB, found" " routes are: %s\n",
1932 logger
.debug("Exiting lib API: {}".format(sys
._getframe
().f_code
.co_name
))
1936 @retry(retry_timeout
=6)
1937 def verify_ospf6_interface(tgen
, topo
=None, dut
=None, lan
=False, input_dict
=None):
1939 This API is to verify ospf routes by running
1940 show ip ospf interface command.
1944 * `tgen` : Topogen object
1945 * `topo` : topology descriptions
1946 * `dut`: device under test
1947 * `lan`: if set to true this interface belongs to LAN.
1948 * `input_dict` : Input dict data, required when configuring from testcase
1960 'mcastMemberOspfDesignatedRouters': True,
1961 'mcastMemberOspfAllRouters': True,
1962 'ospfEnabled': True,
1969 result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
1973 True or False (Error Message)
1976 logger
.debug("Entering lib API: verify_ospf6_interface")
1980 topo
= tgen
.json_topo
1982 for router
, rnode
in tgen
.routers().items():
1983 if "ospf6" not in topo
["routers"][router
]:
1986 if dut
is not None and dut
!= router
:
1989 logger
.info("Verifying OSPF interface on router %s:", router
)
1990 show_ospf_json
= run_frr_cmd(
1991 rnode
, "show ipv6 ospf interface json", isjson
=True
1994 # Verifying output dictionary show_ospf_json is empty or not
1995 if not bool(show_ospf_json
):
1996 errormsg
= "OSPF6 is not running"
1999 # To find neighbor ip type
2000 ospf_intf_data
= input_dict
[router
]["links"]
2001 for ospf_intf
, intf_data
in ospf_intf_data
.items():
2002 intf
= topo
["routers"][router
]["links"][ospf_intf
]["interface"]
2003 if intf
in show_ospf_json
:
2004 for intf_attribute
in intf_data
["ospf6"]:
2005 if intf_data
["ospf6"][intf_attribute
] is not list:
2007 intf_data
["ospf6"][intf_attribute
]
2008 == show_ospf_json
[intf
][intf_attribute
]
2011 "[DUT: %s] OSPF6 interface %s: %s is %s",
2015 intf_data
["ospf6"][intf_attribute
],
2017 elif intf_data
["ospf6"][intf_attribute
] is list:
2018 for addr_list
in len(show_ospf_json
[intf
][intf_attribute
]):
2020 show_ospf_json
[intf
][intf_attribute
][addr_list
][
2023 == intf_data
["ospf6"]["internetAddress"][0]["address"]
2027 errormsg
= "[DUT: {}] OSPF6 interface {}: {} is {}, \
2028 Expected is {}".format(
2032 intf_data
["ospf6"][intf_attribute
],
2033 intf_data
["ospf6"][intf_attribute
],
2037 errormsg
= "[DUT: {}] OSPF6 interface {}: {} is {}, \
2038 Expected is {}".format(
2042 intf_data
["ospf6"][intf_attribute
],
2043 intf_data
["ospf6"][intf_attribute
],
2047 logger
.debug("Exiting lib API: {}".format(sys
._getframe
().f_code
.co_name
))
2051 @retry(retry_timeout
=20)
2052 def verify_ospf6_database(tgen
, topo
, dut
, input_dict
):
2054 This API is to verify ospf lsa's by running
2055 show ip ospf database command.
2059 * `tgen` : Topogen object
2060 * `dut`: device under test
2061 * `input_dict` : Input dict data, required when configuring from testcase
2062 * `topo` : next to be verified
2069 "routerLinkStates": {
2070 "100.1.1.0-100.1.1.0": {
2071 "LSID": "100.1.1.0",
2072 "Advertised router": "100.1.1.0",
2074 "Sequence Number": "80000006",
2079 "networkLinkStates": {
2080 "10.0.0.2-100.1.1.1": {
2082 "Advertised router": "100.1.1.1",
2084 "Sequence Number": "80000001",
2091 result = verify_ospf_database(tgen, topo, dut, input_dict)
2095 True or False (Error Message)
2100 logger
.debug("Entering lib API: {}".format(sys
._getframe
().f_code
.co_name
))
2102 if "ospf" not in topo
["routers"][dut
]:
2103 errormsg
= "[DUT: {}] OSPF is not configured on the router.".format(dut
)
2106 rnode
= tgen
.routers()[dut
]
2108 logger
.info("Verifying OSPF interface on router %s:", dut
)
2109 show_ospf_json
= run_frr_cmd(rnode
, "show ip ospf database json", isjson
=True)
2110 # Verifying output dictionary show_ospf_json is empty or not
2111 if not bool(show_ospf_json
):
2112 errormsg
= "OSPF is not running"
2115 # for inter and inter lsa's
2116 ospf_db_data
= input_dict
.setdefault("areas", None)
2117 ospf_external_lsa
= input_dict
.setdefault("asExternalLinkStates", None)
2120 for ospf_area
, area_lsa
in ospf_db_data
.items():
2121 if ospf_area
in show_ospf_json
["areas"]:
2122 if "routerLinkStates" in area_lsa
:
2123 for lsa
in area_lsa
["routerLinkStates"]:
2124 for rtrlsa
in show_ospf_json
["areas"][ospf_area
][
2128 lsa
["lsaId"] == rtrlsa
["lsaId"]
2129 and lsa
["advertisedRouter"]
2130 == rtrlsa
["advertisedRouter"]
2136 "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s",
2144 "[DUT: {}] OSPF LSDB area {}: expected"
2145 " Router LSA is {}".format(router
, ospf_area
, lsa
)
2149 if "networkLinkStates" in area_lsa
:
2150 for lsa
in area_lsa
["networkLinkStates"]:
2151 for netlsa
in show_ospf_json
["areas"][ospf_area
][
2156 in show_ospf_json
["areas"][ospf_area
][
2161 lsa
["lsaId"] == netlsa
["lsaId"]
2162 and lsa
["advertisedRouter"]
2163 == netlsa
["advertisedRouter"]
2169 "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s",
2177 "[DUT: {}] OSPF LSDB area {}: expected"
2178 " Network LSA is {}".format(router
, ospf_area
, lsa
)
2182 if "summaryLinkStates" in area_lsa
:
2183 for lsa
in area_lsa
["summaryLinkStates"]:
2184 for t3lsa
in show_ospf_json
["areas"][ospf_area
][
2188 lsa
["lsaId"] == t3lsa
["lsaId"]
2189 and lsa
["advertisedRouter"] == t3lsa
["advertisedRouter"]
2195 "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s",
2203 "[DUT: {}] OSPF LSDB area {}: expected"
2204 " Summary LSA is {}".format(router
, ospf_area
, lsa
)
2208 if "nssaExternalLinkStates" in area_lsa
:
2209 for lsa
in area_lsa
["nssaExternalLinkStates"]:
2210 for t7lsa
in show_ospf_json
["areas"][ospf_area
][
2211 "nssaExternalLinkStates"
2214 lsa
["lsaId"] == t7lsa
["lsaId"]
2215 and lsa
["advertisedRouter"] == t7lsa
["advertisedRouter"]
2221 "[DUT: %s] OSPF LSDB area %s:Type7 " "LSA %s",
2229 "[DUT: {}] OSPF LSDB area {}: expected"
2230 " Type7 LSA is {}".format(router
, ospf_area
, lsa
)
2234 if "asbrSummaryLinkStates" in area_lsa
:
2235 for lsa
in area_lsa
["asbrSummaryLinkStates"]:
2236 for t4lsa
in show_ospf_json
["areas"][ospf_area
][
2237 "asbrSummaryLinkStates"
2240 lsa
["lsaId"] == t4lsa
["lsaId"]
2241 and lsa
["advertisedRouter"] == t4lsa
["advertisedRouter"]
2247 "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s",
2255 "[DUT: {}] OSPF LSDB area {}: expected"
2256 " ASBR Summary LSA is {}".format(router
, ospf_area
, lsa
)
2260 if "linkLocalOpaqueLsa" in area_lsa
:
2261 for lsa
in area_lsa
["linkLocalOpaqueLsa"]:
2263 for lnklsa
in show_ospf_json
["areas"][ospf_area
][
2264 "linkLocalOpaqueLsa"
2267 lsa
["lsaId"] in lnklsa
["lsaId"]
2268 and "linkLocalOpaqueLsa"
2269 in show_ospf_json
["areas"][ospf_area
]
2273 "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA"
2282 "[DUT: FRR] OSPF LSDB area: {} "
2283 "expected Opaque-LSA is {}, Found is {}".format(
2284 ospf_area
, lsa
, show_ospf_json
2287 raise ValueError(errormsg
)
2290 errormsg
= "[DUT: FRR] linkLocalOpaqueLsa Not " "present"
2293 if ospf_external_lsa
:
2294 for lsa
in ospf_external_lsa
:
2296 for t5lsa
in show_ospf_json
["asExternalLinkStates"]:
2298 lsa
["lsaId"] == t5lsa
["lsaId"]
2299 and lsa
["advertisedRouter"] == t5lsa
["advertisedRouter"]
2306 logger
.info("[DUT: %s] OSPF LSDB:External LSA %s", router
, lsa
)
2310 "[DUT: {}] OSPF LSDB : expected"
2311 " External LSA is {}".format(router
, lsa
)
2315 logger
.debug("Exiting lib API: {}".format(sys
._getframe
().f_code
.co_name
))
2319 def config_ospf6_interface(
2320 tgen
, topo
=None, input_dict
=None, build
=False, load_config
=True
2323 API to configure ospf on router.
2327 * `tgen` : Topogen object
2328 * `topo` : json file data
2329 * `input_dict` : Input dict data, required when configuring from testcase
2330 * `build` : Only for initial setup phase this is set as True.
2331 * `load_config` : Loading the config to router this is set as True.
2340 "authentication": 'message-digest',
2341 "authentication-key": "ospf",
2342 "message-digest-key": "10"
2348 result = config_ospf6_interface(tgen, topo, r1_ospf_auth)
2355 logger
.debug("Entering lib API: {}".format(sys
._getframe
().f_code
.co_name
))
2358 topo
= tgen
.json_topo
2361 input_dict
= deepcopy(topo
)
2363 input_dict
= deepcopy(input_dict
)
2365 config_data_dict
= {}
2367 for router
in input_dict
.keys():
2369 for lnk
in input_dict
[router
]["links"].keys():
2370 if "ospf6" not in input_dict
[router
]["links"][lnk
]:
2372 "Router %s: ospf6 config is not present in"
2373 "input_dict, passed input_dict %s",
2378 ospf_data
= input_dict
[router
]["links"][lnk
]["ospf6"]
2379 data_ospf_area
= ospf_data
.setdefault("area", None)
2380 data_ospf_auth
= ospf_data
.setdefault("hash-algo", None)
2381 data_ospf_keychain
= ospf_data
.setdefault("keychain", None)
2382 data_ospf_dr_priority
= ospf_data
.setdefault("priority", None)
2383 data_ospf_cost
= ospf_data
.setdefault("cost", None)
2384 data_ospf_mtu
= ospf_data
.setdefault("mtu_ignore", None)
2387 intf
= topo
["routers"][router
]["links"][lnk
]["interface"]
2389 intf
= topo
["switches"][router
]["links"][lnk
]["interface"]
2392 cmd
= "interface {}".format(intf
)
2394 config_data
.append(cmd
)
2395 # interface area config
2397 cmd
= "ipv6 ospf area {}".format(data_ospf_area
)
2398 config_data
.append(cmd
)
2400 # interface ospf auth
2402 cmd
= "ipv6 ospf6 authentication"
2404 if "del_action" in ospf_data
:
2405 cmd
= "no {}".format(cmd
)
2407 if "hash-algo" in ospf_data
:
2408 cmd
= "{} key-id {} hash-algo {} key {}".format(
2410 ospf_data
["key-id"],
2411 ospf_data
["hash-algo"],
2414 config_data
.append(cmd
)
2416 # interface ospf auth with keychain
2417 if data_ospf_keychain
:
2418 cmd
= "ipv6 ospf6 authentication"
2420 if "del_action" in ospf_data
:
2421 cmd
= "no {}".format(cmd
)
2423 if "keychain" in ospf_data
:
2424 cmd
= "{} keychain {}".format(cmd
, ospf_data
["keychain"])
2425 config_data
.append(cmd
)
2427 # interface ospf dr priority
2428 if data_ospf_dr_priority
:
2429 cmd
= "ipv6 ospf priority {}".format(ospf_data
["priority"])
2430 if "del_action" in ospf_data
:
2431 cmd
= "no {}".format(cmd
)
2432 config_data
.append(cmd
)
2434 # interface ospf cost
2436 cmd
= "ipv6 ospf cost {}".format(ospf_data
["cost"])
2437 if "del_action" in ospf_data
:
2438 cmd
= "no {}".format(cmd
)
2439 config_data
.append(cmd
)
2441 # interface ospf mtu
2443 cmd
= "ipv6 ospf mtu-ignore"
2444 if "del_action" in ospf_data
:
2445 cmd
= "no {}".format(cmd
)
2446 config_data
.append(cmd
)
2452 config_data_dict
[router
] = config_data
2454 result
= create_common_configurations(
2455 tgen
, config_data_dict
, "interface_config", build
=build
2458 logger
.debug("Exiting lib API: {}".format(sys
._getframe
().f_code
.co_name
))
2462 @retry(retry_timeout
=20)
2463 def verify_ospf_gr_helper(tgen
, topo
, dut
, input_dict
=None):
2465 This API is used to vreify gr helper using command
2466 show ip ospf graceful-restart helper
2470 * `tgen` : Topogen object
2471 * `topo` : topology descriptions
2473 * 'input_dict' - values to be verified
2478 "helperSupport":"Disabled",
2479 "strictLsaCheck":"Enabled",
2480 "restartSupoort":"Planned and Unplanned Restarts",
2481 "supportedGracePeriod":1800
2483 result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
2486 logger
.debug("Entering lib API: {}".format(sys
._getframe
().f_code
.co_name
))
2489 if "ospf" not in topo
["routers"][dut
]:
2490 errormsg
= "[DUT: {}] OSPF is not configured on the router.".format(dut
)
2493 rnode
= tgen
.routers()[dut
]
2494 logger
.info("Verifying OSPF GR details on router %s:", dut
)
2495 show_ospf_json
= run_frr_cmd(
2496 rnode
, "show ip ospf graceful-restart helper json", isjson
=True
2499 # Verifying output dictionary show_ospf_json is empty or not
2500 if not bool(show_ospf_json
):
2501 errormsg
= "OSPF is not running"
2502 raise ValueError(errormsg
)
2505 for ospf_gr
, gr_data
in input_dict
.items():
2507 if input_dict
[ospf_gr
] == show_ospf_json
[ospf_gr
]:
2509 "[DUT: FRR] OSPF GR Helper: %s is %s",
2511 show_ospf_json
[ospf_gr
],
2516 "[DUT: FRR] OSPF GR Helper: {} expected is {}, Found "
2518 ospf_gr
, input_dict
[ospf_gr
], show_ospf_json
[ospf_gr
]
2521 raise ValueError(errormsg
)
2525 errormsg
= "[DUT: FRR] OSPF GR Helper: {}".format(ospf_gr
)
2528 logger
.debug("Exiting lib API: {}".format(sys
._getframe
().f_code
.co_name
))
2532 def get_ospf_database(tgen
, topo
, dut
, input_dict
, vrf
=None, lsatype
=None, rid
=None):
2534 This API is to return ospf lsa's by running
2535 show ip ospf database command.
2539 * `tgen` : Topogen object
2540 * `dut`: device under test
2541 * `input_dict` : Input dict data, required when configuring from testcase
2542 * `topo` : next to be verified
2543 * `vrf` : vrf to be checked
2544 * `lsatype` : type of lsa to be checked
2545 * `rid` : router id for lsa to be checked
2551 "routerLinkStates": {
2552 "100.1.1.0-100.1.1.0": {
2553 "LSID": "100.1.1.0",
2554 "Advertised router": "100.1.1.0",
2556 "Sequence Number": "80000006",
2561 "networkLinkStates": {
2562 "10.0.0.2-100.1.1.1": {
2564 "Advertised router": "100.1.1.1",
2566 "Sequence Number": "80000001",
2573 result = get_ospf_database(tgen, topo, dut, input_dict)
2577 True or False (Error Message)
2582 logger
.debug("Entering lib API: {}".format(sys
._getframe
().f_code
.co_name
))
2584 if "ospf" not in topo
["routers"][dut
]:
2585 errormsg
= "[DUT: {}] OSPF is not configured on the router.".format(dut
)
2588 rnode
= tgen
.routers()[dut
]
2590 logger
.info("Verifying OSPF interface on router %s:", dut
)
2592 rid
= "self-originate"
2595 command
= "show ip ospf database {} {} json".format(lsatype
, rid
)
2597 command
= "show ip ospf database {} {} vrf {} json".format(
2602 command
= "show ip ospf database json"
2604 command
= "show ip ospf database vrf {} json".format(vrf
)
2606 show_ospf_json
= run_frr_cmd(rnode
, command
, isjson
=True)
2607 # Verifying output dictionary show_ospf_json is empty or not
2608 if not bool(show_ospf_json
):
2609 errormsg
= "OSPF is not running"
2612 # for inter and inter lsa's
2613 ospf_db_data
= input_dict
.setdefault("areas", None)
2614 ospf_external_lsa
= input_dict
.setdefault("asExternalLinkStates", None)
2617 for ospf_area
, area_lsa
in ospf_db_data
.items():
2618 if "areas" in show_ospf_json
and ospf_area
in show_ospf_json
["areas"]:
2619 if "routerLinkStates" in area_lsa
:
2620 for lsa
in area_lsa
["routerLinkStates"]:
2621 for rtrlsa
in show_ospf_json
["areas"][ospf_area
][
2624 _advrtr
= lsa
.setdefault("advertisedRouter", None)
2625 _options
= lsa
.setdefault("options", None)
2628 and lsa
["lsaId"] == rtrlsa
["lsaId"]
2629 and lsa
["advertisedRouter"]
2630 == rtrlsa
["advertisedRouter"]
2636 and lsa
["lsaId"] == rtrlsa
["lsaId"]
2637 and lsa
["options"] == rtrlsa
["options"]
2644 "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s",
2652 "[DUT: {}] OSPF LSDB area {}: expected"
2653 " Router LSA is {}\n found Router LSA: {}".format(
2654 router
, ospf_area
, lsa
, rtrlsa
2659 if "networkLinkStates" in area_lsa
:
2660 for lsa
in area_lsa
["networkLinkStates"]:
2661 for netlsa
in show_ospf_json
["areas"][ospf_area
][
2666 in show_ospf_json
["areas"][ospf_area
][
2671 lsa
["lsaId"] == netlsa
["lsaId"]
2672 and lsa
["advertisedRouter"]
2673 == netlsa
["advertisedRouter"]
2679 "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s",
2687 "[DUT: {}] OSPF LSDB area {}: expected"
2688 " Network LSA is {}".format(router
, ospf_area
, lsa
)
2692 if "summaryLinkStates" in area_lsa
:
2693 for lsa
in area_lsa
["summaryLinkStates"]:
2694 for t3lsa
in show_ospf_json
["areas"][ospf_area
][
2698 lsa
["lsaId"] == t3lsa
["lsaId"]
2699 and lsa
["advertisedRouter"] == t3lsa
["advertisedRouter"]
2705 "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s",
2713 "[DUT: {}] OSPF LSDB area {}: expected"
2714 " Summary LSA is {}".format(router
, ospf_area
, lsa
)
2718 if "nssaExternalLinkStates" in area_lsa
:
2719 for lsa
in area_lsa
["nssaExternalLinkStates"]:
2720 for t7lsa
in show_ospf_json
["areas"][ospf_area
][
2721 "nssaExternalLinkStates"
2724 lsa
["lsaId"] == t7lsa
["lsaId"]
2725 and lsa
["advertisedRouter"] == t7lsa
["advertisedRouter"]
2731 "[DUT: %s] OSPF LSDB area %s:Type7 " "LSA %s",
2739 "[DUT: {}] OSPF LSDB area {}: expected"
2740 " Type7 LSA is {}".format(router
, ospf_area
, lsa
)
2744 if "asbrSummaryLinkStates" in area_lsa
:
2745 for lsa
in area_lsa
["asbrSummaryLinkStates"]:
2746 for t4lsa
in show_ospf_json
["areas"][ospf_area
][
2747 "asbrSummaryLinkStates"
2750 lsa
["lsaId"] == t4lsa
["lsaId"]
2751 and lsa
["advertisedRouter"] == t4lsa
["advertisedRouter"]
2757 "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s",
2765 "[DUT: {}] OSPF LSDB area {}: expected"
2766 " ASBR Summary LSA is {}".format(router
, ospf_area
, lsa
)
2770 if "linkLocalOpaqueLsa" in area_lsa
:
2771 for lsa
in area_lsa
["linkLocalOpaqueLsa"]:
2773 for lnklsa
in show_ospf_json
["areas"][ospf_area
][
2774 "linkLocalOpaqueLsa"
2777 lsa
["lsaId"] in lnklsa
["lsaId"]
2778 and "linkLocalOpaqueLsa"
2779 in show_ospf_json
["areas"][ospf_area
]
2783 "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA"
2792 "[DUT: FRR] OSPF LSDB area: {} "
2793 "expected Opaque-LSA is {}, Found is {}".format(
2794 ospf_area
, lsa
, show_ospf_json
2797 raise ValueError(errormsg
)
2800 errormsg
= "[DUT: FRR] linkLocalOpaqueLsa Not " "present"
2803 if "routerLinkStates" in area_lsa
:
2804 for lsa
in area_lsa
["routerLinkStates"]:
2805 for rtrlsa
in show_ospf_json
["routerLinkStates"]:
2806 _advrtr
= lsa
.setdefault("advertisedRouter", None)
2807 _options
= lsa
.setdefault("options", None)
2808 _age
= lsa
.setdefault("lsaAge", None)
2812 == show_ospf_json
["routerLinkStates"][rtrlsa
][
2821 == show_ospf_json
["routerLinkStates"][rtrlsa
][
2830 show_ospf_json
["routerLinkStates"][rtrlsa
][
2836 "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s",
2844 "[DUT: {}] OSPF LSDB area {}: expected"
2845 " Router LSA is {}\n found Router LSA: {}".format(
2849 show_ospf_json
["routerLinkStates"],
2854 if "networkLinkStates" in area_lsa
:
2855 for lsa
in area_lsa
["networkLinkStates"]:
2856 for netlsa
in show_ospf_json
["areas"][ospf_area
][
2861 in show_ospf_json
["areas"][ospf_area
][
2866 lsa
["lsaId"] == netlsa
["lsaId"]
2867 and lsa
["advertisedRouter"]
2868 == netlsa
["advertisedRouter"]
2874 "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s",
2882 "[DUT: {}] OSPF LSDB area {}: expected"
2883 " Network LSA is {}".format(router
, ospf_area
, lsa
)
2887 if "summaryLinkStates" in area_lsa
:
2888 for lsa
in area_lsa
["summaryLinkStates"]:
2889 for t3lsa
in show_ospf_json
["areas"][ospf_area
][
2893 lsa
["lsaId"] == t3lsa
["lsaId"]
2894 and lsa
["advertisedRouter"] == t3lsa
["advertisedRouter"]
2900 "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s",
2908 "[DUT: {}] OSPF LSDB area {}: expected"
2909 " Summary LSA is {}".format(router
, ospf_area
, lsa
)
2913 if "nssaExternalLinkStates" in area_lsa
:
2914 for lsa
in area_lsa
["nssaExternalLinkStates"]:
2915 for t7lsa
in show_ospf_json
["areas"][ospf_area
][
2916 "nssaExternalLinkStates"
2919 lsa
["lsaId"] == t7lsa
["lsaId"]
2920 and lsa
["advertisedRouter"] == t7lsa
["advertisedRouter"]
2926 "[DUT: %s] OSPF LSDB area %s:Type7 " "LSA %s",
2934 "[DUT: {}] OSPF LSDB area {}: expected"
2935 " Type7 LSA is {}".format(router
, ospf_area
, lsa
)
2939 if "asbrSummaryLinkStates" in area_lsa
:
2940 for lsa
in area_lsa
["asbrSummaryLinkStates"]:
2941 for t4lsa
in show_ospf_json
["areas"][ospf_area
][
2942 "asbrSummaryLinkStates"
2945 lsa
["lsaId"] == t4lsa
["lsaId"]
2946 and lsa
["advertisedRouter"] == t4lsa
["advertisedRouter"]
2952 "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s",
2960 "[DUT: {}] OSPF LSDB area {}: expected"
2961 " ASBR Summary LSA is {}".format(router
, ospf_area
, lsa
)
2965 if "linkLocalOpaqueLsa" in area_lsa
:
2966 for lsa
in area_lsa
["linkLocalOpaqueLsa"]:
2968 for lnklsa
in show_ospf_json
["areas"][ospf_area
][
2969 "linkLocalOpaqueLsa"
2972 lsa
["lsaId"] in lnklsa
["lsaId"]
2973 and "linkLocalOpaqueLsa"
2974 in show_ospf_json
["areas"][ospf_area
]
2978 "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA"
2987 "[DUT: FRR] OSPF LSDB area: {} "
2988 "expected Opaque-LSA is {}, Found is {}".format(
2989 ospf_area
, lsa
, show_ospf_json
2992 raise ValueError(errormsg
)
2995 errormsg
= "[DUT: FRR] linkLocalOpaqueLsa Not " "present"
2998 if ospf_external_lsa
:
2999 for lsa
in ospf_external_lsa
:
3001 for t5lsa
in show_ospf_json
["asExternalLinkStates"]:
3003 lsa
["lsaId"] == t5lsa
["lsaId"]
3004 and lsa
["advertisedRouter"] == t5lsa
["advertisedRouter"]
3011 logger
.info("[DUT: %s] OSPF LSDB:External LSA %s", router
, lsa
)
3015 "[DUT: {}] OSPF LSDB : expected"
3016 " External LSA is {}".format(router
, lsa
)
3020 logger
.debug("Exiting lib API: {}".format(sys
._getframe
().f_code
.co_name
))