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