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