]>
Commit | Line | Data |
---|---|---|
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 | ||
21 | from copy import deepcopy | |
22 | import traceback | |
23 | from time import sleep | |
24 | from lib.topolog import logger | |
25 | import ipaddr | |
61196140 | 26 | from lib.topotest import frr_unicode |
4256a209 | 27 | |
28 | # Import common_config to use commomnly used APIs | |
701a0192 | 29 | from 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 | |
39 | LOGDIR = "/tmp/topotests/" | |
40 | TMPDIR = None | |
41 | ||
42 | ################################ | |
43 | # Configure procs | |
44 | ################################ | |
45 | ||
701a0192 | 46 | |
47 | def 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 | 97 | def __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 | 217 | def 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 | 260 | def __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 | |
314 | def 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 | 438 | def 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) | |
471 | def 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 | 662 | def 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 | 926 | def 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 | 1015 | def 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) |
1178 | def 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 |