]>
Commit | Line | Data |
---|---|---|
e8cd26fd | 1 | # Copyright (c) 2019 by VMware, Inc. ("VMware") |
2 | # Used Copyright (c) 2018 by Network Device Education Foundation, Inc. | |
3 | # ("NetDEF") in this file. | |
4 | # | |
5 | # Permission to use, copy, modify, and/or distribute this software | |
6 | # for any purpose with or without fee is hereby granted, provided | |
7 | # that the above copyright notice and this permission notice appear | |
8 | # in all copies. | |
9 | # | |
10 | # THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES | |
11 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR | |
13 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY | |
14 | # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
15 | # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS | |
16 | # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |
17 | # OF THIS SOFTWARE. | |
18 | ||
49581587 | 19 | import datetime |
e8cd26fd | 20 | import os |
21 | import re | |
49581587 | 22 | import sys |
e8cd26fd | 23 | import traceback |
3d7d6e9a | 24 | import functools |
e8cd26fd | 25 | from copy import deepcopy |
49581587 | 26 | from time import sleep |
3d7d6e9a | 27 | from lib import topotest |
49581587 | 28 | |
e8cd26fd | 29 | |
30 | # Import common_config to use commomnly used APIs | |
31 | from lib.common_config import ( | |
4f99894d | 32 | create_common_configurations, |
a5124c49 CH |
33 | HostApplicationHelper, |
34 | InvalidCLIError, | |
49581587 | 35 | create_common_configuration, |
e8cd26fd | 36 | InvalidCLIError, |
37 | retry, | |
38 | run_frr_cmd, | |
e13f9c4f | 39 | validate_ip_address, |
d7032129 | 40 | get_frr_ipv6_linklocal, |
e8cd26fd | 41 | ) |
4953ca97 | 42 | from lib.micronet import get_exec_path |
49581587 | 43 | from lib.topolog import logger |
a5124c49 | 44 | from lib.topotest import frr_unicode |
e8cd26fd | 45 | |
46 | #### | |
47 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
48 | ||
49 | ||
50 | def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True): | |
51 | """ | |
d7032129 | 52 | API to configure pim/pim6 on router |
e8cd26fd | 53 | |
54 | Parameters | |
55 | ---------- | |
56 | * `tgen` : Topogen object | |
57 | * `topo` : json file data | |
58 | * `input_dict` : Input dict data, required when configuring from | |
59 | testcase | |
60 | * `build` : Only for initial setup phase this is set as True. | |
61 | ||
62 | Usage | |
63 | ----- | |
64 | input_dict = { | |
65 | "r1": { | |
66 | "pim": { | |
0a76e764 | 67 | "join-prune-interval": "5", |
e8cd26fd | 68 | "rp": [{ |
69 | "rp_addr" : "1.0.3.17". | |
70 | "keep-alive-timer": "100" | |
71 | "group_addr_range": ["224.1.1.0/24", "225.1.1.0/24"] | |
72 | "prefix-list": "pf_list_1" | |
73 | "delete": True | |
74 | }] | |
e13f9c4f KK |
75 | }, |
76 | "pim6": { | |
77 | "disable" : ["l1-i1-eth1"], | |
78 | "rp": [{ | |
79 | "rp_addr" : "2001:db8:f::5:17". | |
80 | "keep-alive-timer": "100" | |
81 | "group_addr_range": ["FF00::/8"] | |
82 | "prefix-list": "pf_list_1" | |
83 | "delete": True | |
84 | }] | |
e8cd26fd | 85 | } |
86 | } | |
87 | } | |
88 | ||
89 | ||
90 | Returns | |
91 | ------- | |
92 | True or False | |
93 | """ | |
94 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
95 | result = False | |
96 | if not input_dict: | |
97 | input_dict = deepcopy(topo) | |
98 | else: | |
99 | topo = topo["routers"] | |
100 | input_dict = deepcopy(input_dict) | |
4f99894d CH |
101 | |
102 | config_data_dict = {} | |
103 | ||
e8cd26fd | 104 | for router in input_dict.keys(): |
4f99894d | 105 | config_data = _enable_disable_pim_config(tgen, topo, input_dict, router, build) |
e8cd26fd | 106 | |
4f99894d CH |
107 | if config_data: |
108 | config_data_dict[router] = config_data | |
109 | ||
110 | # Now add RP config to all routers | |
111 | for router in input_dict.keys(): | |
e13f9c4f KK |
112 | if "pim" in input_dict[router] or "pim6" in input_dict[router]: |
113 | _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict) | |
4f99894d CH |
114 | try: |
115 | result = create_common_configurations( | |
116 | tgen, config_data_dict, "pim", build, load_config | |
117 | ) | |
118 | except InvalidCLIError: | |
119 | logger.error("create_pim_config", exc_info=True) | |
120 | result = False | |
e8cd26fd | 121 | |
122 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
123 | return result | |
124 | ||
125 | ||
4f99894d | 126 | def _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict): |
e8cd26fd | 127 | """ |
0a76e764 | 128 | Helper API to create pim RP configurations. |
e8cd26fd | 129 | |
130 | Parameters | |
131 | ---------- | |
132 | * `tgen` : Topogen object | |
133 | * `topo` : json file data | |
134 | * `input_dict` : Input dict data, required when configuring from testcase | |
135 | * `router` : router id to be configured. | |
136 | * `build` : Only for initial setup phase this is set as True. | |
4f99894d | 137 | * `config_data_dict` : OUT: adds `router` config to dictinary |
e8cd26fd | 138 | Returns |
139 | ------- | |
4f99894d | 140 | None |
e8cd26fd | 141 | """ |
142 | ||
e8cd26fd | 143 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) |
e13f9c4f KK |
144 | rp_data = [] |
145 | ||
146 | # PIMv4 | |
147 | pim_data = None | |
148 | if "pim" in input_dict[router]: | |
149 | pim_data = input_dict[router]["pim"] | |
150 | if "rp" in input_dict[router]["pim"]: | |
151 | rp_data += pim_data["rp"] | |
e8cd26fd | 152 | |
d7032129 | 153 | # pim6 |
e13f9c4f KK |
154 | pim6_data = None |
155 | if "pim6" in input_dict[router]: | |
156 | pim6_data = input_dict[router]["pim6"] | |
157 | if "rp" in input_dict[router]["pim6"]: | |
158 | rp_data += pim6_data["rp"] | |
e8cd26fd | 159 | |
0a76e764 CH |
160 | # Configure this RP on every router. |
161 | for dut in tgen.routers(): | |
0a76e764 CH |
162 | # At least one interface must be enabled for PIM on the router |
163 | pim_if_enabled = False | |
e13f9c4f | 164 | pim6_if_enabled = False |
0a76e764 CH |
165 | for destLink, data in topo[dut]["links"].items(): |
166 | if "pim" in data: | |
167 | pim_if_enabled = True | |
e13f9c4f KK |
168 | if "pim6" in data: |
169 | pim6_if_enabled = True | |
170 | if not pim_if_enabled and pim_data: | |
171 | continue | |
172 | if not pim6_if_enabled and pim6_data: | |
0a76e764 | 173 | continue |
e8cd26fd | 174 | |
0a76e764 | 175 | config_data = [] |
e8cd26fd | 176 | |
e13f9c4f KK |
177 | if rp_data: |
178 | for rp_dict in deepcopy(rp_data): | |
179 | # ip address of RP | |
180 | if "rp_addr" not in rp_dict and build: | |
181 | logger.error( | |
182 | "Router %s: 'ip address of RP' not " | |
183 | "present in input_dict/JSON", | |
184 | router, | |
185 | ) | |
e8cd26fd | 186 | |
e13f9c4f KK |
187 | return False |
188 | rp_addr = rp_dict.setdefault("rp_addr", None) | |
189 | if rp_addr: | |
190 | addr_type = validate_ip_address(rp_addr) | |
191 | # Keep alive Timer | |
192 | keep_alive_timer = rp_dict.setdefault("keep_alive_timer", None) | |
193 | ||
194 | # Group Address range to cover | |
195 | if "group_addr_range" not in rp_dict and build: | |
196 | logger.error( | |
197 | "Router %s:'Group Address range to cover'" | |
198 | " not present in input_dict/JSON", | |
199 | router, | |
200 | ) | |
e8cd26fd | 201 | |
e13f9c4f KK |
202 | return False |
203 | group_addr_range = rp_dict.setdefault("group_addr_range", None) | |
e8cd26fd | 204 | |
e13f9c4f KK |
205 | # Group prefix-list filter |
206 | prefix_list = rp_dict.setdefault("prefix_list", None) | |
e8cd26fd | 207 | |
e13f9c4f KK |
208 | # Delete rp config |
209 | del_action = rp_dict.setdefault("delete", False) | |
0a76e764 | 210 | |
e13f9c4f KK |
211 | if keep_alive_timer: |
212 | if addr_type == "ipv4": | |
213 | cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer) | |
214 | if del_action: | |
215 | cmd = "no {}".format(cmd) | |
216 | config_data.append(cmd) | |
217 | if addr_type == "ipv6": | |
218 | cmd = "ipv6 pim rp keep-alive-timer {}".format(keep_alive_timer) | |
e8cd26fd | 219 | if del_action: |
220 | cmd = "no {}".format(cmd) | |
0a76e764 | 221 | config_data.append(cmd) |
e8cd26fd | 222 | |
e13f9c4f KK |
223 | if rp_addr: |
224 | if group_addr_range: | |
225 | if type(group_addr_range) is not list: | |
226 | group_addr_range = [group_addr_range] | |
e8cd26fd | 227 | |
e13f9c4f KK |
228 | for grp_addr in group_addr_range: |
229 | if addr_type == "ipv4": | |
230 | cmd = "ip pim rp {} {}".format(rp_addr, grp_addr) | |
231 | if del_action: | |
232 | cmd = "no {}".format(cmd) | |
233 | config_data.append(cmd) | |
234 | if addr_type == "ipv6": | |
235 | cmd = "ipv6 pim rp {} {}".format(rp_addr, grp_addr) | |
236 | if del_action: | |
237 | cmd = "no {}".format(cmd) | |
238 | config_data.append(cmd) | |
239 | ||
240 | if prefix_list: | |
241 | if addr_type == "ipv4": | |
242 | cmd = "ip pim rp {} prefix-list {}".format( | |
243 | rp_addr, prefix_list | |
244 | ) | |
245 | if del_action: | |
246 | cmd = "no {}".format(cmd) | |
247 | config_data.append(cmd) | |
248 | if addr_type == "ipv6": | |
249 | cmd = "ipv6 pim rp {} prefix-list {}".format( | |
250 | rp_addr, prefix_list | |
251 | ) | |
252 | if del_action: | |
253 | cmd = "no {}".format(cmd) | |
254 | config_data.append(cmd) | |
255 | ||
256 | if config_data: | |
257 | if dut not in config_data_dict: | |
258 | config_data_dict[dut] = config_data | |
259 | else: | |
260 | config_data_dict[dut].extend(config_data) | |
e8cd26fd | 261 | |
262 | ||
263 | def create_igmp_config(tgen, topo, input_dict=None, build=False): | |
264 | """ | |
265 | API to configure igmp on router | |
266 | ||
267 | Parameters | |
268 | ---------- | |
269 | * `tgen` : Topogen object | |
270 | * `topo` : json file data | |
271 | * `input_dict` : Input dict data, required when configuring from | |
272 | testcase | |
273 | * `build` : Only for initial setup phase this is set as True. | |
274 | ||
275 | Usage | |
276 | ----- | |
277 | input_dict = { | |
278 | "r1": { | |
279 | "igmp": { | |
280 | "interfaces": { | |
281 | "r1-r0-eth0" :{ | |
282 | "igmp":{ | |
283 | "version": "2", | |
284 | "delete": True | |
285 | "query": { | |
286 | "query-interval" : 100, | |
287 | "query-max-response-time": 200 | |
288 | } | |
289 | } | |
290 | } | |
291 | } | |
292 | } | |
293 | } | |
294 | } | |
295 | ||
296 | Returns | |
297 | ------- | |
298 | True or False | |
299 | """ | |
300 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
301 | result = False | |
302 | if not input_dict: | |
303 | input_dict = deepcopy(topo) | |
304 | else: | |
305 | topo = topo["routers"] | |
306 | input_dict = deepcopy(input_dict) | |
4f99894d CH |
307 | |
308 | config_data_dict = {} | |
309 | ||
e8cd26fd | 310 | for router in input_dict.keys(): |
311 | if "igmp" not in input_dict[router]: | |
312 | logger.debug("Router %s: 'igmp' is not present in " "input_dict", router) | |
313 | continue | |
314 | ||
315 | igmp_data = input_dict[router]["igmp"] | |
316 | ||
317 | if "interfaces" in igmp_data: | |
318 | config_data = [] | |
319 | intf_data = igmp_data["interfaces"] | |
320 | ||
321 | for intf_name in intf_data.keys(): | |
322 | cmd = "interface {}".format(intf_name) | |
323 | config_data.append(cmd) | |
324 | protocol = "igmp" | |
325 | del_action = intf_data[intf_name]["igmp"].setdefault("delete", False) | |
58cfbb27 | 326 | del_attr = intf_data[intf_name]["igmp"].setdefault("delete_attr", False) |
e8cd26fd | 327 | cmd = "ip igmp" |
328 | if del_action: | |
329 | cmd = "no {}".format(cmd) | |
58cfbb27 KK |
330 | if not del_attr: |
331 | config_data.append(cmd) | |
e8cd26fd | 332 | |
e8cd26fd | 333 | for attribute, data in intf_data[intf_name]["igmp"].items(): |
334 | if attribute == "version": | |
335 | cmd = "ip {} {} {}".format(protocol, attribute, data) | |
336 | if del_action: | |
337 | cmd = "no {}".format(cmd) | |
58cfbb27 KK |
338 | if not del_attr: |
339 | config_data.append(cmd) | |
e8cd26fd | 340 | |
341 | if attribute == "join": | |
342 | for group in data: | |
343 | cmd = "ip {} {} {}".format(protocol, attribute, group) | |
344 | if del_attr: | |
345 | cmd = "no {}".format(cmd) | |
346 | config_data.append(cmd) | |
347 | ||
348 | if attribute == "query": | |
349 | for query, value in data.items(): | |
350 | if query != "delete": | |
351 | cmd = "ip {} {} {}".format(protocol, query, value) | |
352 | ||
353 | if "delete" in intf_data[intf_name][protocol]["query"]: | |
354 | cmd = "no {}".format(cmd) | |
355 | ||
356 | config_data.append(cmd) | |
4f99894d CH |
357 | if config_data: |
358 | config_data_dict[router] = config_data | |
e8cd26fd | 359 | |
4f99894d CH |
360 | try: |
361 | result = create_common_configurations( | |
362 | tgen, config_data_dict, "interface_config", build=build | |
363 | ) | |
364 | except InvalidCLIError: | |
365 | logger.error("create_igmp_config", exc_info=True) | |
366 | result = False | |
e8cd26fd | 367 | |
368 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
369 | return result | |
370 | ||
371 | ||
e13f9c4f KK |
372 | def create_mld_config(tgen, topo, input_dict=None, build=False): |
373 | """ | |
d7032129 | 374 | API to configure mld for pim6 on router |
e13f9c4f KK |
375 | |
376 | Parameters | |
377 | ---------- | |
378 | * `tgen` : Topogen object | |
379 | * `topo` : json file data | |
380 | * `input_dict` : Input dict data, required when configuring from | |
381 | testcase | |
382 | * `build` : Only for initial setup phase this is set as True. | |
383 | ||
384 | Usage | |
385 | ----- | |
386 | input_dict = { | |
387 | "r1": { | |
388 | "mld": { | |
389 | "interfaces": { | |
390 | "r1-r0-eth0" :{ | |
391 | "mld":{ | |
392 | "version": "2", | |
393 | "delete": True | |
394 | "query": { | |
395 | "query-interval" : 100, | |
396 | "query-max-response-time": 200 | |
397 | } | |
398 | } | |
399 | } | |
400 | } | |
401 | } | |
402 | } | |
403 | } | |
404 | ||
405 | Returns | |
406 | ------- | |
407 | True or False | |
408 | """ | |
409 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
410 | result = False | |
411 | if not input_dict: | |
412 | input_dict = deepcopy(topo) | |
413 | else: | |
414 | topo = topo["routers"] | |
415 | input_dict = deepcopy(input_dict) | |
416 | for router in input_dict.keys(): | |
417 | if "mld" not in input_dict[router]: | |
418 | logger.debug("Router %s: 'mld' is not present in " "input_dict", router) | |
419 | continue | |
420 | ||
421 | mld_data = input_dict[router]["mld"] | |
422 | ||
423 | if "interfaces" in mld_data: | |
424 | config_data = [] | |
425 | intf_data = mld_data["interfaces"] | |
426 | ||
427 | for intf_name in intf_data.keys(): | |
428 | cmd = "interface {}".format(intf_name) | |
429 | config_data.append(cmd) | |
430 | protocol = "mld" | |
431 | del_action = intf_data[intf_name]["mld"].setdefault("delete", False) | |
432 | cmd = "ipv6 mld" | |
433 | if del_action: | |
434 | cmd = "no {}".format(cmd) | |
435 | config_data.append(cmd) | |
436 | ||
437 | del_attr = intf_data[intf_name]["mld"].setdefault("delete_attr", False) | |
438 | join = intf_data[intf_name]["mld"].setdefault("join", None) | |
439 | source = intf_data[intf_name]["mld"].setdefault("source", None) | |
440 | version = intf_data[intf_name]["mld"].setdefault("version", False) | |
441 | query = intf_data[intf_name]["mld"].setdefault("query", {}) | |
442 | ||
443 | if version: | |
444 | cmd = "ipv6 {} version {}".format(protocol, version) | |
445 | if del_action: | |
446 | cmd = "no {}".format(cmd) | |
447 | config_data.append(cmd) | |
448 | ||
449 | if source and join: | |
450 | for group in join: | |
451 | cmd = "ipv6 {} join {} {}".format(protocol, group, source) | |
452 | ||
453 | if del_attr: | |
454 | cmd = "no {}".format(cmd) | |
455 | config_data.append(cmd) | |
456 | ||
457 | elif join: | |
458 | for group in join: | |
459 | cmd = "ipv6 {} join {}".format(protocol, group) | |
460 | ||
461 | if del_attr: | |
462 | cmd = "no {}".format(cmd) | |
463 | config_data.append(cmd) | |
464 | ||
465 | if query: | |
466 | for _query, value in query.items(): | |
467 | if _query != "delete": | |
468 | cmd = "ipv6 {} {} {}".format(protocol, _query, value) | |
469 | ||
470 | if "delete" in intf_data[intf_name][protocol]["query"]: | |
471 | cmd = "no {}".format(cmd) | |
472 | ||
473 | config_data.append(cmd) | |
474 | try: | |
475 | result = create_common_configuration( | |
476 | tgen, router, config_data, "interface_config", build=build | |
477 | ) | |
478 | except InvalidCLIError: | |
479 | errormsg = traceback.format_exc() | |
480 | logger.error(errormsg) | |
481 | return errormsg | |
482 | ||
483 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
484 | return result | |
485 | ||
486 | ||
4f99894d | 487 | def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False): |
e8cd26fd | 488 | """ |
489 | Helper API to enable or disable pim on interfaces | |
490 | ||
491 | Parameters | |
492 | ---------- | |
493 | * `tgen` : Topogen object | |
494 | * `topo` : json file data | |
495 | * `input_dict` : Input dict data, required when configuring from testcase | |
496 | * `router` : router id to be configured. | |
497 | * `build` : Only for initial setup phase this is set as True. | |
498 | ||
499 | Returns | |
500 | ------- | |
4f99894d | 501 | list of config |
e8cd26fd | 502 | """ |
e8cd26fd | 503 | |
4f99894d | 504 | config_data = [] |
e8cd26fd | 505 | |
e13f9c4f | 506 | # Enable pim/pim6 on interfaces |
4f99894d CH |
507 | for destRouterLink, data in sorted(topo[router]["links"].items()): |
508 | if "pim" in data and data["pim"] == "enable": | |
509 | # Loopback interfaces | |
510 | if "type" in data and data["type"] == "loopback": | |
511 | interface_name = destRouterLink | |
512 | else: | |
513 | interface_name = data["interface"] | |
514 | ||
515 | cmd = "interface {}".format(interface_name) | |
516 | config_data.append(cmd) | |
517 | config_data.append("ip pim") | |
518 | ||
d7032129 KK |
519 | if "pim" in input_dict[router]: |
520 | if "disable" in input_dict[router]["pim"]: | |
521 | enable_flag = False | |
522 | interfaces = input_dict[router]["pim"]["disable"] | |
523 | ||
524 | if type(interfaces) is not list: | |
525 | interfaces = [interfaces] | |
526 | ||
527 | for interface in interfaces: | |
528 | cmd = "interface {}".format(interface) | |
529 | config_data.append(cmd) | |
530 | config_data.append("no ip pim") | |
531 | ||
e13f9c4f KK |
532 | if "pim6" in data and data["pim6"] == "enable": |
533 | # Loopback interfaces | |
534 | if "type" in data and data["type"] == "loopback": | |
535 | interface_name = destRouterLink | |
536 | else: | |
537 | interface_name = data["interface"] | |
538 | ||
539 | cmd = "interface {}".format(interface_name) | |
540 | config_data.append(cmd) | |
541 | config_data.append("ipv6 pim") | |
542 | ||
d7032129 KK |
543 | if "pim6" in input_dict[router]: |
544 | if "disable" in input_dict[router]["pim6"]: | |
545 | enable_flag = False | |
546 | interfaces = input_dict[router]["pim6"]["disable"] | |
547 | ||
548 | if type(interfaces) is not list: | |
549 | interfaces = [interfaces] | |
550 | ||
551 | for interface in interfaces: | |
552 | cmd = "interface {}".format(interface) | |
553 | config_data.append(cmd) | |
554 | config_data.append("no ipv6 pim") | |
555 | ||
4f99894d CH |
556 | # pim global config |
557 | if "pim" in input_dict[router]: | |
558 | pim_data = input_dict[router]["pim"] | |
559 | del_action = pim_data.setdefault("delete", False) | |
560 | for t in [ | |
a53c08bc CH |
561 | "join-prune-interval", |
562 | "keep-alive-timer", | |
563 | "register-suppress-time", | |
4f99894d CH |
564 | ]: |
565 | if t in pim_data: | |
566 | cmd = "ip pim {} {}".format(t, pim_data[t]) | |
567 | if del_action: | |
568 | cmd = "no {}".format(cmd) | |
0a76e764 | 569 | config_data.append(cmd) |
e8cd26fd | 570 | |
e13f9c4f KK |
571 | # pim6 global config |
572 | if "pim6" in input_dict[router]: | |
573 | pim6_data = input_dict[router]["pim6"] | |
574 | del_action = pim6_data.setdefault("delete", False) | |
575 | for t in [ | |
576 | "join-prune-interval", | |
577 | "keep-alive-timer", | |
578 | "register-suppress-time", | |
579 | ]: | |
580 | if t in pim6_data: | |
581 | cmd = "ipv6 pim {} {}".format(t, pim6_data[t]) | |
582 | if del_action: | |
583 | cmd = "no {}".format(cmd) | |
584 | config_data.append(cmd) | |
585 | ||
4f99894d | 586 | return config_data |
e8cd26fd | 587 | |
588 | ||
e8cd26fd | 589 | def find_rp_details(tgen, topo): |
590 | """ | |
591 | Find who is RP in topology and returns list of RPs | |
592 | ||
593 | Parameters: | |
594 | ----------- | |
595 | * `tgen` : Topogen object | |
596 | * `topo` : json file data | |
597 | ||
598 | returns: | |
599 | -------- | |
600 | errormsg or True | |
601 | """ | |
602 | ||
603 | rp_details = {} | |
604 | ||
605 | router_list = tgen.routers() | |
606 | topo_data = topo["routers"] | |
607 | ||
608 | for router in router_list.keys(): | |
609 | ||
610 | if "pim" not in topo_data[router]: | |
611 | continue | |
612 | ||
613 | pim_data = topo_data[router]["pim"] | |
614 | if "rp" in pim_data: | |
615 | rp_data = pim_data["rp"] | |
616 | for rp_dict in rp_data: | |
617 | # ip address of RP | |
618 | rp_addr = rp_dict["rp_addr"] | |
619 | ||
620 | for link, data in topo["routers"][router]["links"].items(): | |
621 | if data["ipv4"].split("/")[0] == rp_addr: | |
622 | rp_details[router] = rp_addr | |
623 | ||
624 | return rp_details | |
625 | ||
626 | ||
627 | def configure_pim_force_expire(tgen, topo, input_dict, build=False): | |
628 | """ | |
629 | Helper API to create pim configuration. | |
630 | ||
631 | Parameters | |
632 | ---------- | |
633 | * `tgen` : Topogen object | |
634 | * `topo` : json file data | |
635 | * `input_dict` : Input dict data, required when configuring from testcase | |
636 | * `build` : Only for initial setup phase this is set as True. | |
637 | ||
638 | Usage | |
639 | ----- | |
640 | input_dict ={ | |
641 | "l1": { | |
642 | "pim": { | |
643 | "force_expire":{ | |
644 | "10.0.10.1": ["255.1.1.1"] | |
645 | } | |
646 | } | |
647 | } | |
648 | } | |
649 | ||
650 | result = create_pim_config(tgen, topo, input_dict) | |
651 | ||
652 | Returns | |
653 | ------- | |
654 | True or False | |
655 | """ | |
656 | ||
657 | result = False | |
658 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
4f99894d | 659 | |
e8cd26fd | 660 | try: |
4f99894d | 661 | config_data_dict = {} |
e8cd26fd | 662 | |
663 | for dut in input_dict.keys(): | |
664 | if "pim" not in input_dict[dut]: | |
665 | continue | |
666 | ||
667 | pim_data = input_dict[dut]["pim"] | |
668 | ||
4f99894d | 669 | config_data = [] |
e8cd26fd | 670 | if "force_expire" in pim_data: |
e8cd26fd | 671 | force_expire_data = pim_data["force_expire"] |
672 | ||
673 | for source, groups in force_expire_data.items(): | |
674 | if type(groups) is not list: | |
675 | groups = [groups] | |
676 | ||
677 | for group in groups: | |
678 | cmd = "ip pim force-expire source {} group {}".format( | |
679 | source, group | |
680 | ) | |
681 | config_data.append(cmd) | |
682 | ||
4f99894d CH |
683 | if config_data: |
684 | config_data_dict[dut] = config_data | |
e8cd26fd | 685 | |
4f99894d CH |
686 | result = create_common_configurations( |
687 | tgen, config_data_dict, "pim", build=build | |
688 | ) | |
e8cd26fd | 689 | except InvalidCLIError: |
4f99894d CH |
690 | logger.error("configure_pim_force_expire", exc_info=True) |
691 | result = False | |
e8cd26fd | 692 | |
693 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
694 | return result | |
695 | ||
696 | ||
697 | ############################################# | |
698 | # Verification APIs | |
699 | ############################################# | |
ed776e38 | 700 | @retry(retry_timeout=12) |
3c41ebf8 | 701 | def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None, expected=True): |
e8cd26fd | 702 | """ |
703 | Verify all PIM neighbors are up and running, config is verified | |
704 | using "show ip pim neighbor" cli | |
705 | ||
706 | Parameters | |
707 | ---------- | |
708 | * `tgen`: topogen object | |
709 | * `topo` : json file data | |
710 | * `dut` : dut info | |
711 | * `iface` : link for which PIM nbr need to check | |
eab72dc8 | 712 | * `nbr_ip` : neighbor ip of interface |
3c41ebf8 | 713 | * `expected` : expected results from API, by-default True |
e8cd26fd | 714 | |
715 | Usage | |
716 | ----- | |
eab72dc8 | 717 | result = verify_pim_neighbors(tgen, topo, dut, iface=ens192, nbr_ip=20.1.1.2) |
e8cd26fd | 718 | |
719 | Returns | |
720 | ------- | |
721 | errormsg(str) or True | |
722 | """ | |
723 | ||
724 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
725 | ||
726 | for router in tgen.routers(): | |
727 | if dut is not None and dut != router: | |
728 | continue | |
729 | ||
730 | rnode = tgen.routers()[router] | |
731 | show_ip_pim_neighbor_json = rnode.vtysh_cmd( | |
732 | "show ip pim neighbor json", isjson=True | |
733 | ) | |
734 | ||
735 | for destLink, data in topo["routers"][router]["links"].items(): | |
736 | if iface is not None and iface != data["interface"]: | |
737 | continue | |
738 | ||
739 | if "type" in data and data["type"] == "loopback": | |
740 | continue | |
741 | ||
742 | if "pim" not in data: | |
743 | continue | |
744 | ||
697ce62f KK |
745 | if "pim" in data and data["pim"] == "disable": |
746 | continue | |
747 | ||
e8cd26fd | 748 | if "pim" in data and data["pim"] == "enable": |
749 | local_interface = data["interface"] | |
750 | ||
751 | if "-" in destLink: | |
752 | # Spliting and storing destRouterLink data in tempList | |
753 | tempList = destLink.split("-") | |
754 | ||
755 | # destRouter | |
756 | destLink = tempList.pop(0) | |
757 | ||
758 | # Current Router Link | |
759 | tempList.insert(0, router) | |
760 | curRouter = "-".join(tempList) | |
761 | else: | |
762 | curRouter = router | |
763 | if destLink not in topo["routers"]: | |
764 | continue | |
765 | data = topo["routers"][destLink]["links"][curRouter] | |
766 | if "type" in data and data["type"] == "loopback": | |
767 | continue | |
768 | ||
769 | if "pim" not in data: | |
770 | continue | |
771 | ||
772 | logger.info("[DUT: %s]: Verifying PIM neighbor status:", router) | |
773 | ||
774 | if "pim" in data and data["pim"] == "enable": | |
775 | pim_nh_intf_ip = data["ipv4"].split("/")[0] | |
776 | ||
777 | # Verifying PIM neighbor | |
778 | if local_interface in show_ip_pim_neighbor_json: | |
779 | if show_ip_pim_neighbor_json[local_interface]: | |
780 | if ( | |
781 | show_ip_pim_neighbor_json[local_interface][pim_nh_intf_ip][ | |
782 | "neighbor" | |
783 | ] | |
784 | != pim_nh_intf_ip | |
785 | ): | |
786 | errormsg = ( | |
787 | "[DUT %s]: Local interface: %s, PIM" | |
788 | " neighbor check failed " | |
789 | "Expected neighbor: %s, Found neighbor:" | |
790 | " %s" | |
791 | % ( | |
792 | router, | |
793 | local_interface, | |
794 | pim_nh_intf_ip, | |
795 | show_ip_pim_neighbor_json[local_interface][ | |
796 | pim_nh_intf_ip | |
797 | ]["neighbor"], | |
798 | ) | |
799 | ) | |
800 | return errormsg | |
801 | ||
802 | logger.info( | |
803 | "[DUT %s]: Local interface: %s, Found" | |
804 | " expected PIM neighbor %s", | |
805 | router, | |
806 | local_interface, | |
807 | pim_nh_intf_ip, | |
808 | ) | |
809 | else: | |
810 | errormsg = ( | |
811 | "[DUT %s]: Local interface: %s, and" | |
812 | "interface ip: %s is not found in " | |
813 | "PIM neighbor " % (router, local_interface, pim_nh_intf_ip) | |
814 | ) | |
815 | return errormsg | |
816 | else: | |
817 | errormsg = ( | |
818 | "[DUT %s]: Local interface: %s, is not " | |
819 | "present in PIM neighbor " % (router, local_interface) | |
820 | ) | |
821 | return errormsg | |
822 | ||
823 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
824 | return True | |
825 | ||
826 | ||
d7032129 KK |
827 | @retry(retry_timeout=12) |
828 | def verify_pim6_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None, expected=True): | |
829 | """ | |
830 | Verify all pim6 neighbors are up and running, config is verified | |
831 | using "show ipv6 pim neighbor" cli | |
832 | ||
833 | Parameters | |
834 | ---------- | |
835 | * `tgen`: topogen object | |
836 | * `topo` : json file data | |
837 | * `dut` : dut info | |
838 | * `iface` : link for which PIM nbr need to check | |
839 | * `nbr_ip` : neighbor ip of interface | |
840 | * `expected` : expected results from API, by-default True | |
841 | ||
842 | Usage | |
843 | ----- | |
844 | result = verify_pim6_neighbors(tgen, topo, dut, iface=ens192, nbr_ip=20.1.1.2) | |
845 | ||
846 | Returns | |
847 | ------- | |
848 | errormsg(str) or True | |
849 | """ | |
850 | ||
851 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
852 | ||
853 | for router in tgen.routers(): | |
854 | if dut is not None and dut != router: | |
855 | continue | |
856 | ||
857 | rnode = tgen.routers()[router] | |
858 | show_ip_pim_neighbor_json = rnode.vtysh_cmd( | |
859 | "show ipv6 pim neighbor json", isjson=True | |
860 | ) | |
861 | ||
862 | for destLink, data in topo["routers"][router]["links"].items(): | |
863 | if "type" in data and data["type"] == "loopback": | |
864 | continue | |
865 | ||
866 | if iface is not None and iface != data["interface"]: | |
867 | continue | |
868 | ||
869 | if "pim6" not in data: | |
870 | continue | |
871 | ||
872 | if "pim6" in data and data["pim6"] == "disable": | |
873 | continue | |
874 | ||
875 | if "pim6" in data and data["pim6"] == "enable": | |
876 | local_interface = data["interface"] | |
877 | ||
878 | if "-" in destLink: | |
879 | # Spliting and storing destRouterLink data in tempList | |
880 | tempList = destLink.split("-") | |
881 | ||
882 | # destRouter | |
883 | destLink = tempList.pop(0) | |
884 | ||
885 | # Current Router Link | |
886 | tempList.insert(0, router) | |
887 | curRouter = "-".join(tempList) | |
888 | else: | |
889 | curRouter = router | |
890 | if destLink not in topo["routers"]: | |
891 | continue | |
892 | data = topo["routers"][destLink]["links"][curRouter] | |
893 | peer_interface = data["interface"] | |
894 | if "type" in data and data["type"] == "loopback": | |
895 | continue | |
896 | ||
897 | if "pim6" not in data: | |
898 | continue | |
899 | ||
900 | logger.info("[DUT: %s]: Verifying PIM neighbor status:", router) | |
901 | ||
902 | if "pim6" in data and data["pim6"] == "enable": | |
903 | pim_nh_intf_ip = get_frr_ipv6_linklocal(tgen, destLink, peer_interface) | |
904 | ||
905 | # Verifying PIM neighbor | |
906 | if local_interface in show_ip_pim_neighbor_json: | |
907 | if show_ip_pim_neighbor_json[local_interface]: | |
908 | if ( | |
909 | show_ip_pim_neighbor_json[local_interface][pim_nh_intf_ip][ | |
910 | "neighbor" | |
911 | ] | |
912 | != pim_nh_intf_ip | |
913 | ): | |
914 | errormsg = ( | |
915 | "[DUT %s]: Local interface: %s, PIM6" | |
916 | " neighbor check failed " | |
917 | "Expected neighbor: %s, Found neighbor:" | |
918 | " %s" | |
919 | % ( | |
920 | router, | |
921 | local_interface, | |
922 | pim_nh_intf_ip, | |
923 | show_ip_pim_neighbor_json[local_interface][ | |
924 | pim_nh_intf_ip | |
925 | ]["neighbor"], | |
926 | ) | |
927 | ) | |
928 | return errormsg | |
929 | ||
930 | logger.info( | |
931 | "[DUT %s]: Local interface: %s, Found" | |
932 | " expected PIM6 neighbor %s", | |
933 | router, | |
934 | local_interface, | |
935 | pim_nh_intf_ip, | |
936 | ) | |
937 | else: | |
938 | errormsg = ( | |
939 | "[DUT %s]: Local interface: %s, and" | |
940 | "interface ip: %s is not found in " | |
941 | "PIM6 neighbor " % (router, local_interface, pim_nh_intf_ip) | |
942 | ) | |
943 | return errormsg | |
944 | else: | |
945 | errormsg = ( | |
946 | "[DUT %s]: Local interface: %s, is not " | |
947 | "present in PIM6 neighbor " % (router, local_interface) | |
948 | ) | |
949 | return errormsg | |
950 | ||
951 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
952 | return True | |
953 | ||
954 | ||
de3d6b4d | 955 | @retry(retry_timeout=40, diag_pct=0) |
3c41ebf8 | 956 | def verify_igmp_groups(tgen, dut, interface, group_addresses, expected=True): |
e8cd26fd | 957 | """ |
958 | Verify IGMP groups are received from an intended interface | |
959 | by running "show ip igmp groups" command | |
960 | ||
961 | Parameters | |
962 | ---------- | |
963 | * `tgen`: topogen object | |
964 | * `dut`: device under test | |
965 | * `interface`: interface, from which IGMP groups would be received | |
966 | * `group_addresses`: IGMP group address | |
3c41ebf8 | 967 | * `expected` : expected results from API, by-default True |
e8cd26fd | 968 | |
969 | Usage | |
970 | ----- | |
971 | dut = "r1" | |
972 | interface = "r1-r0-eth0" | |
973 | group_address = "225.1.1.1" | |
974 | result = verify_igmp_groups(tgen, dut, interface, group_address) | |
975 | ||
976 | Returns | |
977 | ------- | |
978 | errormsg(str) or True | |
979 | """ | |
980 | ||
981 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
982 | ||
983 | if dut not in tgen.routers(): | |
984 | return False | |
985 | ||
986 | rnode = tgen.routers()[dut] | |
987 | ||
988 | logger.info("[DUT: %s]: Verifying IGMP groups received:", dut) | |
989 | show_ip_igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True) | |
990 | ||
991 | if type(group_addresses) is not list: | |
992 | group_addresses = [group_addresses] | |
993 | ||
994 | if interface in show_ip_igmp_json: | |
995 | show_ip_igmp_json = show_ip_igmp_json[interface]["groups"] | |
996 | else: | |
997 | errormsg = ( | |
998 | "[DUT %s]: Verifying IGMP group received" | |
999 | " from interface %s [FAILED]!! " % (dut, interface) | |
1000 | ) | |
1001 | return errormsg | |
1002 | ||
1003 | found = False | |
1004 | for grp_addr in group_addresses: | |
1005 | for index in show_ip_igmp_json: | |
1006 | if index["group"] == grp_addr: | |
1007 | found = True | |
1008 | break | |
1009 | if found is not True: | |
1010 | errormsg = ( | |
1011 | "[DUT %s]: Verifying IGMP group received" | |
1012 | " from interface %s [FAILED]!! " | |
1013 | " Expected not found: %s" % (dut, interface, grp_addr) | |
1014 | ) | |
1015 | return errormsg | |
1016 | ||
1017 | logger.info( | |
1018 | "[DUT %s]: Verifying IGMP group %s received " | |
1019 | "from interface %s [PASSED]!! ", | |
1020 | dut, | |
1021 | grp_addr, | |
1022 | interface, | |
1023 | ) | |
1024 | ||
1025 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1026 | return True | |
1027 | ||
1028 | ||
d7032129 | 1029 | @retry(retry_timeout=60, diag_pct=2) |
e8cd26fd | 1030 | def verify_upstream_iif( |
a53c08bc CH |
1031 | tgen, |
1032 | dut, | |
1033 | iif, | |
1034 | src_address, | |
1035 | group_addresses, | |
1036 | joinState=None, | |
d7032129 | 1037 | regState=None, |
a53c08bc | 1038 | refCount=1, |
d7032129 | 1039 | addr_type="ipv4", |
a53c08bc | 1040 | expected=True, |
e8cd26fd | 1041 | ): |
1042 | """ | |
1043 | Verify upstream inbound interface is updated correctly | |
1044 | by running "show ip pim upstream" cli | |
1045 | ||
1046 | Parameters | |
1047 | ---------- | |
1048 | * `tgen`: topogen object | |
1049 | * `dut`: device under test | |
1050 | * `iif`: inbound interface | |
1051 | * `src_address`: source address | |
1052 | * `group_addresses`: IGMP group address | |
1053 | * `joinState`: upstream join state | |
1054 | * `refCount`: refCount value | |
3c41ebf8 | 1055 | * `expected` : expected results from API, by-default True |
e8cd26fd | 1056 | |
1057 | Usage | |
1058 | ----- | |
1059 | dut = "r1" | |
1060 | iif = "r1-r0-eth0" | |
1061 | src_address = "*" | |
1062 | group_address = "225.1.1.1" | |
1063 | result = verify_upstream_iif(tgen, dut, iif, src_address, group_address, | |
1064 | state, refCount) | |
1065 | ||
1066 | Returns | |
1067 | ------- | |
1068 | errormsg(str) or True | |
1069 | """ | |
e8cd26fd | 1070 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) |
1071 | ||
1072 | if dut not in tgen.routers(): | |
1073 | return False | |
1074 | ||
1075 | rnode = tgen.routers()[dut] | |
1076 | ||
1077 | logger.info( | |
d7032129 KK |
1078 | "[DUT: %s]: Verifying upstream Inbound Interface" |
1079 | " for IGMP/MLD groups received:", | |
e8cd26fd | 1080 | dut, |
1081 | ) | |
e8cd26fd | 1082 | |
1083 | if type(group_addresses) is not list: | |
1084 | group_addresses = [group_addresses] | |
1085 | ||
1086 | if type(iif) is not list: | |
1087 | iif = [iif] | |
1088 | ||
e13f9c4f KK |
1089 | for grp in group_addresses: |
1090 | addr_type = validate_ip_address(grp) | |
1091 | ||
1092 | if addr_type == "ipv4": | |
1093 | ip_cmd = "ip" | |
1094 | elif addr_type == "ipv6": | |
1095 | ip_cmd = "ipv6" | |
1096 | ||
1097 | cmd = "show {} pim upstream json".format(ip_cmd) | |
1098 | show_ip_pim_upstream_json = run_frr_cmd(rnode, cmd, isjson=True) | |
1099 | ||
e8cd26fd | 1100 | for grp_addr in group_addresses: |
1101 | # Verify group address | |
1102 | if grp_addr not in show_ip_pim_upstream_json: | |
1103 | errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % ( | |
1104 | dut, | |
1105 | grp_addr, | |
1106 | ) | |
1107 | return errormsg | |
1108 | group_addr_json = show_ip_pim_upstream_json[grp_addr] | |
1109 | ||
1110 | # Verify source address | |
1111 | if src_address not in group_addr_json: | |
1112 | errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % ( | |
1113 | dut, | |
1114 | src_address, | |
1115 | grp_addr, | |
1116 | ) | |
1117 | return errormsg | |
1118 | ||
1119 | # Verify Inbound Interface | |
1120 | found = False | |
1121 | for in_interface in iif: | |
1122 | if group_addr_json[src_address]["inboundInterface"] == in_interface: | |
1123 | if refCount > 0: | |
1124 | logger.info( | |
1125 | "[DUT %s]: Verifying refCount " | |
1126 | "for (%s,%s) [PASSED]!! " | |
1127 | " Found Expected: %s", | |
1128 | dut, | |
1129 | src_address, | |
1130 | grp_addr, | |
1131 | group_addr_json[src_address]["refCount"], | |
1132 | ) | |
1133 | found = True | |
1134 | if found: | |
1135 | if joinState is None: | |
1136 | if group_addr_json[src_address]["joinState"] != "Joined": | |
1137 | errormsg = ( | |
1138 | "[DUT %s]: Verifying iif " | |
db726bb8 KK |
1139 | "(Inbound Interface) and joinState " |
1140 | "for (%s, %s), Expected iif: %s, " | |
1141 | "Found iif : %s, and Expected " | |
1142 | "joinState :%s , Found joinState: %s" | |
e8cd26fd | 1143 | % ( |
1144 | dut, | |
1145 | src_address, | |
1146 | grp_addr, | |
e8cd26fd | 1147 | in_interface, |
1148 | group_addr_json[src_address]["inboundInterface"], | |
db726bb8 KK |
1149 | joinState, |
1150 | group_addr_json[src_address]["joinState"] | |
e8cd26fd | 1151 | ) |
1152 | ) | |
1153 | return errormsg | |
1154 | ||
1155 | elif group_addr_json[src_address]["joinState"] != joinState: | |
1156 | errormsg = ( | |
1157 | "[DUT %s]: Verifying iif " | |
db726bb8 KK |
1158 | "(Inbound Interface) and joinState " |
1159 | "for (%s, %s), Expected iif: %s, " | |
1160 | "Found iif : %s, and Expected " | |
1161 | "joinState :%s , Found joinState: %s" | |
e8cd26fd | 1162 | % ( |
1163 | dut, | |
1164 | src_address, | |
1165 | grp_addr, | |
e8cd26fd | 1166 | in_interface, |
1167 | group_addr_json[src_address]["inboundInterface"], | |
db726bb8 KK |
1168 | joinState, |
1169 | group_addr_json[src_address]["joinState"] | |
e8cd26fd | 1170 | ) |
1171 | ) | |
1172 | return errormsg | |
1173 | ||
d7032129 KK |
1174 | if regState: |
1175 | if group_addr_json[src_address]["regState"] != regState: | |
1176 | errormsg = ( | |
1177 | "[DUT %s]: Verifying iif " | |
db726bb8 KK |
1178 | "(Inbound Interface) and regState " |
1179 | "for (%s, %s), Expected iif: %s, " | |
1180 | "Found iif : %s, and Expected " | |
1181 | "regState :%s , Found regState: %s" | |
d7032129 KK |
1182 | % ( |
1183 | dut, | |
1184 | src_address, | |
1185 | grp_addr, | |
d7032129 KK |
1186 | in_interface, |
1187 | group_addr_json[src_address]["inboundInterface"], | |
db726bb8 KK |
1188 | regState, |
1189 | group_addr_json[src_address]["regState"] | |
d7032129 KK |
1190 | ) |
1191 | ) | |
1192 | return errormsg | |
1193 | ||
e8cd26fd | 1194 | logger.info( |
1195 | "[DUT %s]: Verifying iif(Inbound Interface)" | |
d7032129 | 1196 | " for (%s,%s) and joinState is %s regstate is %s [PASSED]!! " |
e8cd26fd | 1197 | " Found Expected: (%s)", |
1198 | dut, | |
1199 | src_address, | |
1200 | grp_addr, | |
1201 | group_addr_json[src_address]["joinState"], | |
d7032129 | 1202 | group_addr_json[src_address]["regState"], |
e8cd26fd | 1203 | group_addr_json[src_address]["inboundInterface"], |
1204 | ) | |
1205 | if not found: | |
1206 | errormsg = ( | |
1207 | "[DUT %s]: Verifying iif " | |
1208 | "(Inbound Interface) for (%s, %s) " | |
1209 | "[FAILED]!! " | |
1210 | " Expected: %s, Found: %s" | |
1211 | % ( | |
1212 | dut, | |
1213 | src_address, | |
1214 | grp_addr, | |
1215 | in_interface, | |
1216 | group_addr_json[src_address]["inboundInterface"], | |
1217 | ) | |
1218 | ) | |
1219 | return errormsg | |
1220 | ||
1221 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1222 | return True | |
1223 | ||
1224 | ||
ed776e38 | 1225 | @retry(retry_timeout=12) |
a53c08bc | 1226 | def verify_join_state_and_timer( |
d7032129 | 1227 | tgen, dut, iif, src_address, group_addresses, addr_type="ipv4", expected=True |
a53c08bc | 1228 | ): |
e8cd26fd | 1229 | """ |
1230 | Verify join state is updated correctly and join timer is | |
1231 | running with the help of "show ip pim upstream" cli | |
1232 | ||
1233 | Parameters | |
1234 | ---------- | |
1235 | * `tgen`: topogen object | |
1236 | * `dut`: device under test | |
1237 | * `iif`: inbound interface | |
1238 | * `src_address`: source address | |
1239 | * `group_addresses`: IGMP group address | |
3c41ebf8 | 1240 | * `expected` : expected results from API, by-default True |
e8cd26fd | 1241 | |
1242 | Usage | |
1243 | ----- | |
1244 | dut = "r1" | |
1245 | iif = "r1-r0-eth0" | |
1246 | group_address = "225.1.1.1" | |
1247 | result = verify_join_state_and_timer(tgen, dut, iif, group_address) | |
1248 | ||
1249 | Returns | |
1250 | ------- | |
1251 | errormsg(str) or True | |
1252 | """ | |
1253 | ||
1254 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1255 | errormsg = "" | |
1256 | ||
1257 | if dut not in tgen.routers(): | |
1258 | return False | |
1259 | ||
1260 | rnode = tgen.routers()[dut] | |
1261 | ||
1262 | logger.info( | |
1263 | "[DUT: %s]: Verifying Join state and Join Timer" " for IGMP groups received:", | |
1264 | dut, | |
1265 | ) | |
e8cd26fd | 1266 | |
1267 | if type(group_addresses) is not list: | |
1268 | group_addresses = [group_addresses] | |
1269 | ||
e13f9c4f KK |
1270 | for grp in group_addresses: |
1271 | addr_type = validate_ip_address(grp) | |
1272 | ||
1273 | if addr_type == "ipv4": | |
1274 | cmd = "show ip pim upstream json" | |
1275 | elif addr_type == "ipv6": | |
1276 | cmd = "show ipv6 pim upstream json" | |
1277 | show_ip_pim_upstream_json = run_frr_cmd(rnode, cmd, isjson=True) | |
1278 | ||
e8cd26fd | 1279 | for grp_addr in group_addresses: |
1280 | # Verify group address | |
1281 | if grp_addr not in show_ip_pim_upstream_json: | |
1282 | errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % ( | |
1283 | dut, | |
1284 | grp_addr, | |
1285 | ) | |
1286 | return errormsg | |
1287 | ||
1288 | group_addr_json = show_ip_pim_upstream_json[grp_addr] | |
1289 | ||
1290 | # Verify source address | |
1291 | if src_address not in group_addr_json: | |
1292 | errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % ( | |
1293 | dut, | |
1294 | src_address, | |
1295 | grp_addr, | |
1296 | ) | |
1297 | return errormsg | |
1298 | ||
1299 | # Verify join state | |
1300 | joinState = group_addr_json[src_address]["joinState"] | |
1301 | if joinState != "Joined": | |
1302 | error = ( | |
1303 | "[DUT %s]: Verifying join state for" | |
1304 | " (%s,%s) [FAILED]!! " | |
1305 | " Expected: %s, Found: %s" | |
1306 | % (dut, src_address, grp_addr, "Joined", joinState) | |
1307 | ) | |
1308 | errormsg = errormsg + "\n" + str(error) | |
1309 | else: | |
1310 | logger.info( | |
1311 | "[DUT %s]: Verifying join state for" | |
1312 | " (%s,%s) [PASSED]!! " | |
1313 | " Found Expected: %s", | |
1314 | dut, | |
1315 | src_address, | |
1316 | grp_addr, | |
1317 | joinState, | |
1318 | ) | |
1319 | ||
1320 | # Verify join timer | |
1321 | joinTimer = group_addr_json[src_address]["joinTimer"] | |
1322 | if not re.match(r"(\d{2}):(\d{2}):(\d{2})", joinTimer): | |
1323 | error = ( | |
1324 | "[DUT %s]: Verifying join timer for" | |
1325 | " (%s,%s) [FAILED]!! " | |
49581587 CH |
1326 | " Expected: %s, Found: %s" |
1327 | ) % ( | |
e8cd26fd | 1328 | dut, |
1329 | src_address, | |
1330 | grp_addr, | |
1331 | "join timer should be running", | |
1332 | joinTimer, | |
1333 | ) | |
1334 | errormsg = errormsg + "\n" + str(error) | |
1335 | else: | |
1336 | logger.info( | |
1337 | "[DUT %s]: Verifying join timer is running" | |
1338 | " for (%s,%s) [PASSED]!! " | |
1339 | " Found Expected: %s", | |
1340 | dut, | |
1341 | src_address, | |
1342 | grp_addr, | |
1343 | joinTimer, | |
1344 | ) | |
1345 | ||
1346 | if errormsg != "": | |
1347 | return errormsg | |
1348 | ||
1349 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1350 | return True | |
1351 | ||
1352 | ||
de3d6b4d | 1353 | @retry(retry_timeout=120, diag_pct=0) |
4fafd29f | 1354 | def verify_mroutes( |
a53c08bc CH |
1355 | tgen, |
1356 | dut, | |
1357 | src_address, | |
1358 | group_addresses, | |
1359 | iif, | |
1360 | oil, | |
1361 | return_uptime=False, | |
1362 | mwait=0, | |
d7032129 | 1363 | addr_type="ipv4", |
a53c08bc | 1364 | expected=True, |
e8cd26fd | 1365 | ): |
1366 | """ | |
1367 | Verify ip mroutes and make sure (*, G)/(S, G) is present in mroutes | |
4fafd29f | 1368 | by running "show ip/ipv6 mroute" cli |
e8cd26fd | 1369 | |
1370 | Parameters | |
1371 | ---------- | |
1372 | * `tgen`: topogen object | |
1373 | * `dut`: device under test | |
1374 | * `src_address`: source address | |
1375 | * `group_addresses`: IGMP group address | |
1376 | * `iif`: Incoming interface | |
1377 | * `oil`: Outgoing interface | |
1378 | * `return_uptime`: If True, return uptime dict, default is False | |
1379 | * `mwait`: Wait time, default is 0 | |
3c41ebf8 | 1380 | * `expected` : expected results from API, by-default True |
e8cd26fd | 1381 | |
1382 | Usage | |
1383 | ----- | |
1384 | dut = "r1" | |
1385 | group_address = "225.1.1.1" | |
4fafd29f | 1386 | result = verify_mroutes(tgen, dut, src_address, group_address) |
e8cd26fd | 1387 | |
1388 | Returns | |
1389 | ------- | |
1390 | errormsg(str) or True | |
1391 | """ | |
1392 | ||
1393 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1394 | ||
1395 | if dut not in tgen.routers(): | |
1396 | return False | |
1397 | ||
1398 | rnode = tgen.routers()[dut] | |
1399 | ||
e13f9c4f KK |
1400 | if not isinstance(group_addresses, list): |
1401 | group_addresses = [group_addresses] | |
1402 | ||
1403 | if not isinstance(iif, list) and iif != "none": | |
1404 | iif = [iif] | |
1405 | ||
1406 | if not isinstance(oil, list) and oil != "none": | |
1407 | oil = [oil] | |
1408 | ||
1409 | for grp in group_addresses: | |
1410 | addr_type = validate_ip_address(grp) | |
1411 | ||
1412 | if addr_type == "ipv4": | |
1413 | ip_cmd = "ip" | |
1414 | elif addr_type == "ipv6": | |
1415 | ip_cmd = "ipv6" | |
1416 | ||
e8cd26fd | 1417 | if return_uptime: |
1418 | logger.info("Sleeping for %s sec..", mwait) | |
1419 | sleep(mwait) | |
1420 | ||
1421 | logger.info("[DUT: %s]: Verifying ip mroutes", dut) | |
e13f9c4f KK |
1422 | show_ip_mroute_json = run_frr_cmd( |
1423 | rnode, "show {} mroute json".format(ip_cmd), isjson=True | |
1424 | ) | |
e8cd26fd | 1425 | |
1426 | if return_uptime: | |
1427 | uptime_dict = {} | |
1428 | ||
1429 | if bool(show_ip_mroute_json) == False: | |
1430 | error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut) | |
1431 | return error_msg | |
1432 | ||
e8cd26fd | 1433 | for grp_addr in group_addresses: |
1434 | if grp_addr not in show_ip_mroute_json: | |
1435 | errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % ( | |
1436 | dut, | |
1437 | src_address, | |
1438 | grp_addr, | |
1439 | ) | |
1440 | return errormsg | |
1441 | else: | |
1442 | if return_uptime: | |
1443 | uptime_dict[grp_addr] = {} | |
1444 | ||
1445 | group_addr_json = show_ip_mroute_json[grp_addr] | |
1446 | ||
1447 | if src_address not in group_addr_json: | |
1448 | errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % ( | |
1449 | dut, | |
1450 | src_address, | |
1451 | grp_addr, | |
1452 | ) | |
1453 | return errormsg | |
1454 | else: | |
1455 | if return_uptime: | |
1456 | uptime_dict[grp_addr][src_address] = {} | |
1457 | ||
1458 | mroutes = group_addr_json[src_address] | |
1459 | ||
1460 | if mroutes["installed"] != 0: | |
1461 | logger.info( | |
1462 | "[DUT %s]: mroute (%s,%s) is installed", dut, src_address, grp_addr | |
1463 | ) | |
1464 | ||
1465 | if "oil" not in mroutes: | |
1466 | if oil == "none" and mroutes["iif"] in iif: | |
1467 | logger.info( | |
1468 | "[DUT %s]: Verifying (%s, %s) mroute," | |
1469 | " [PASSED]!! Found Expected: " | |
1470 | "(iif: %s, oil: %s, installed: (%s,%s))", | |
1471 | dut, | |
1472 | src_address, | |
1473 | grp_addr, | |
1474 | mroutes["iif"], | |
1475 | oil, | |
1476 | src_address, | |
1477 | grp_addr, | |
1478 | ) | |
1479 | else: | |
1480 | errormsg = ( | |
1481 | "[DUT %s]: Verifying (%s, %s) mroute," | |
1482 | " [FAILED]!! " | |
1483 | "Expected: (oil: %s, installed:" | |
1484 | " (%s,%s)) Found: ( oil: none, " | |
1485 | "installed: (%s,%s))" | |
1486 | % ( | |
1487 | dut, | |
1488 | src_address, | |
1489 | grp_addr, | |
1490 | oil, | |
1491 | src_address, | |
1492 | grp_addr, | |
1493 | src_address, | |
1494 | grp_addr, | |
1495 | ) | |
1496 | ) | |
1497 | ||
1498 | return errormsg | |
1499 | ||
1500 | else: | |
1501 | found = False | |
1502 | for route, data in mroutes["oil"].items(): | |
1503 | if route in oil and route != "pimreg": | |
1504 | if ( | |
1505 | data["source"] == src_address | |
1506 | and data["group"] == grp_addr | |
1507 | and data["inboundInterface"] in iif | |
1508 | and data["outboundInterface"] in oil | |
1509 | ): | |
1510 | if return_uptime: | |
1511 | ||
1512 | uptime_dict[grp_addr][src_address] = data["upTime"] | |
1513 | ||
1514 | logger.info( | |
1515 | "[DUT %s]: Verifying (%s, %s)" | |
1516 | " mroute, [PASSED]!! " | |
1517 | "Found Expected: " | |
1518 | "(iif: %s, oil: %s, installed:" | |
1519 | " (%s,%s)", | |
1520 | dut, | |
1521 | src_address, | |
1522 | grp_addr, | |
1523 | data["inboundInterface"], | |
1524 | data["outboundInterface"], | |
1525 | data["source"], | |
1526 | data["group"], | |
1527 | ) | |
1528 | found = True | |
1529 | break | |
1530 | else: | |
1531 | continue | |
1532 | ||
1533 | if not found: | |
1534 | errormsg = ( | |
1535 | "[DUT %s]: Verifying (%s, %s)" | |
1536 | " mroute [FAILED]!! " | |
1537 | "Expected in: (iif: %s, oil: %s," | |
1538 | " installed: (%s,%s)) Found: " | |
1539 | "(iif: %s, oil: %s, " | |
1540 | "installed: (%s,%s))" | |
1541 | % ( | |
1542 | dut, | |
1543 | src_address, | |
1544 | grp_addr, | |
1545 | iif, | |
1546 | oil, | |
1547 | src_address, | |
1548 | grp_addr, | |
1549 | data["inboundInterface"], | |
1550 | data["outboundInterface"], | |
1551 | data["source"], | |
1552 | data["group"], | |
1553 | ) | |
1554 | ) | |
1555 | return errormsg | |
1556 | ||
1557 | else: | |
1558 | errormsg = "[DUT %s]: mroute (%s,%s) is not installed" % ( | |
1559 | dut, | |
1560 | src_address, | |
1561 | grp_addr, | |
1562 | ) | |
1563 | return errormsg | |
1564 | ||
1565 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1566 | return True if return_uptime == False else uptime_dict | |
1567 | ||
1568 | ||
de3d6b4d | 1569 | @retry(retry_timeout=60, diag_pct=0) |
e8cd26fd | 1570 | def verify_pim_rp_info( |
a53c08bc CH |
1571 | tgen, |
1572 | topo, | |
1573 | dut, | |
1574 | group_addresses, | |
1575 | oif=None, | |
1576 | rp=None, | |
1577 | source=None, | |
1578 | iamrp=None, | |
d7032129 | 1579 | addr_type="ipv4", |
a53c08bc | 1580 | expected=True, |
e8cd26fd | 1581 | ): |
1582 | """ | |
1583 | Verify pim rp info by running "show ip pim rp-info" cli | |
1584 | ||
1585 | Parameters | |
1586 | ---------- | |
1587 | * `tgen`: topogen object | |
1588 | * `topo`: JSON file handler | |
1589 | * `dut`: device under test | |
1590 | * `group_addresses`: IGMP group address | |
1591 | * `oif`: outbound interface name | |
1592 | * `rp`: RP address | |
1593 | * `source`: Source of RP | |
1594 | * `iamrp`: User defined RP | |
3c41ebf8 | 1595 | * `expected` : expected results from API, by-default True |
e8cd26fd | 1596 | |
1597 | Usage | |
1598 | ----- | |
1599 | dut = "r1" | |
1600 | result = verify_pim_rp_info(tgen, topo, dut, group_address, | |
1601 | rp=rp, source="BSR") | |
1602 | ||
1603 | Returns | |
1604 | ------- | |
1605 | errormsg(str) or True | |
1606 | """ | |
1607 | ||
1608 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1609 | ||
1610 | if dut not in tgen.routers(): | |
1611 | return False | |
1612 | ||
1613 | rnode = tgen.routers()[dut] | |
1614 | ||
e8cd26fd | 1615 | if type(group_addresses) is not list: |
1616 | group_addresses = [group_addresses] | |
1617 | ||
1618 | if type(oif) is not list: | |
1619 | oif = [oif] | |
1620 | ||
e13f9c4f KK |
1621 | for grp in group_addresses: |
1622 | addr_type = validate_ip_address(grp) | |
1623 | ||
1624 | if addr_type == "ipv4": | |
1625 | ip_cmd = "ip" | |
1626 | elif addr_type == "ipv6": | |
1627 | ip_cmd = "ipv6" | |
1628 | ||
e8cd26fd | 1629 | for grp_addr in group_addresses: |
1630 | if rp is None: | |
1631 | rp_details = find_rp_details(tgen, topo) | |
1632 | ||
1633 | if dut in rp_details: | |
1634 | iamRP = True | |
1635 | else: | |
1636 | iamRP = False | |
1637 | else: | |
e13f9c4f KK |
1638 | if addr_type == "ipv4": |
1639 | show_ip_route_json = run_frr_cmd( | |
1640 | rnode, "show ip route connected json", isjson=True | |
1641 | ) | |
1642 | elif addr_type == "ipv6": | |
1643 | show_ip_route_json = run_frr_cmd( | |
1644 | rnode, "show ipv6 route connected json", isjson=True | |
1645 | ) | |
e8cd26fd | 1646 | for _rp in show_ip_route_json.keys(): |
1647 | if rp == _rp.split("/")[0]: | |
1648 | iamRP = True | |
1649 | break | |
1650 | else: | |
1651 | iamRP = False | |
1652 | ||
e13f9c4f KK |
1653 | logger.info("[DUT: %s]: Verifying ip rp info", dut) |
1654 | cmd = "show {} pim rp-info json".format(ip_cmd) | |
1655 | show_ip_rp_info_json = run_frr_cmd(rnode, cmd, isjson=True) | |
1656 | ||
e8cd26fd | 1657 | if rp not in show_ip_rp_info_json: |
e13f9c4f KK |
1658 | errormsg = ( |
1659 | "[DUT %s]: Verifying rp-info " | |
1660 | "for rp_address %s [FAILED]!! " % (dut, rp) | |
e8cd26fd | 1661 | ) |
1662 | return errormsg | |
1663 | else: | |
1664 | group_addr_json = show_ip_rp_info_json[rp] | |
1665 | ||
1666 | for rp_json in group_addr_json: | |
e13f9c4f KK |
1667 | if "rpAddress" not in rp_json: |
1668 | errormsg = "[DUT %s]: %s key not " "present in rp-info " % ( | |
1669 | dut, | |
1670 | "rpAddress", | |
1671 | ) | |
1672 | return errormsg | |
1673 | ||
e8cd26fd | 1674 | if oif is not None: |
1675 | found = False | |
1676 | if rp_json["outboundInterface"] not in oif: | |
1677 | errormsg = ( | |
1678 | "[DUT %s]: Verifying OIF " | |
1679 | "for group %s and RP %s [FAILED]!! " | |
1680 | "Expected interfaces: (%s)," | |
1681 | " Found: (%s)" | |
1682 | % (dut, grp_addr, rp, oif, rp_json["outboundInterface"]) | |
1683 | ) | |
1684 | return errormsg | |
1685 | ||
1686 | logger.info( | |
1687 | "[DUT %s]: Verifying OIF " | |
1688 | "for group %s and RP %s [PASSED]!! " | |
1689 | "Found Expected: (%s)" | |
1690 | % (dut, grp_addr, rp, rp_json["outboundInterface"]) | |
1691 | ) | |
1692 | ||
1693 | if source is not None: | |
1694 | if rp_json["source"] != source: | |
1695 | errormsg = ( | |
1696 | "[DUT %s]: Verifying SOURCE " | |
1697 | "for group %s and RP %s [FAILED]!! " | |
1698 | "Expected: (%s)," | |
1699 | " Found: (%s)" % (dut, grp_addr, rp, source, rp_json["source"]) | |
1700 | ) | |
1701 | return errormsg | |
1702 | ||
1703 | logger.info( | |
1704 | "[DUT %s]: Verifying SOURCE " | |
1705 | "for group %s and RP %s [PASSED]!! " | |
1706 | "Found Expected: (%s)" % (dut, grp_addr, rp, rp_json["source"]) | |
1707 | ) | |
1708 | ||
1709 | if rp_json["group"] == grp_addr and iamrp is not None: | |
1710 | if iamRP: | |
1711 | if rp_json["iAmRP"]: | |
1712 | logger.info( | |
1713 | "[DUT %s]: Verifying group " | |
1714 | "and iAmRP [PASSED]!!" | |
1715 | " Found Expected: (%s, %s:%s)", | |
1716 | dut, | |
1717 | grp_addr, | |
1718 | "iAmRP", | |
1719 | rp_json["iAmRP"], | |
1720 | ) | |
1721 | else: | |
1722 | errormsg = ( | |
1723 | "[DUT %s]: Verifying group" | |
1724 | "%s and iAmRP [FAILED]!! " | |
1725 | "Expected: (iAmRP: %s)," | |
1726 | " Found: (iAmRP: %s)" | |
1727 | % (dut, grp_addr, "true", rp_json["iAmRP"]) | |
1728 | ) | |
1729 | return errormsg | |
1730 | ||
1731 | if not iamRP: | |
1732 | if rp_json["iAmRP"] == False: | |
1733 | logger.info( | |
1734 | "[DUT %s]: Verifying group " | |
1735 | "and iAmNotRP [PASSED]!!" | |
1736 | " Found Expected: (%s, %s:%s)", | |
1737 | dut, | |
1738 | grp_addr, | |
1739 | "iAmRP", | |
1740 | rp_json["iAmRP"], | |
1741 | ) | |
1742 | else: | |
1743 | errormsg = ( | |
1744 | "[DUT %s]: Verifying group" | |
1745 | "%s and iAmRP [FAILED]!! " | |
1746 | "Expected: (iAmRP: %s)," | |
1747 | " Found: (iAmRP: %s)" | |
1748 | % (dut, grp_addr, "false", rp_json["iAmRP"]) | |
1749 | ) | |
1750 | return errormsg | |
1751 | ||
1752 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1753 | return True | |
1754 | ||
1755 | ||
de3d6b4d | 1756 | @retry(retry_timeout=60, diag_pct=0) |
e8cd26fd | 1757 | def verify_pim_state( |
a53c08bc CH |
1758 | tgen, |
1759 | dut, | |
1760 | iif, | |
1761 | oil, | |
1762 | group_addresses, | |
1763 | src_address=None, | |
1764 | installed_fl=None, | |
d7032129 | 1765 | addr_type="ipv4", |
a53c08bc | 1766 | expected=True, |
e8cd26fd | 1767 | ): |
1768 | """ | |
1769 | Verify pim state by running "show ip pim state" cli | |
1770 | ||
1771 | Parameters | |
1772 | ---------- | |
1773 | * `tgen`: topogen object | |
1774 | * `dut`: device under test | |
1775 | * `iif`: inbound interface | |
1776 | * `oil`: outbound interface | |
1777 | * `group_addresses`: IGMP group address | |
1778 | * `src_address`: source address, default = None | |
1779 | * installed_fl` : Installed flag | |
3c41ebf8 | 1780 | * `expected` : expected results from API, by-default True |
e8cd26fd | 1781 | |
1782 | Usage | |
1783 | ----- | |
1784 | dut = "r1" | |
1785 | iif = "r1-r3-eth1" | |
1786 | oil = "r1-r0-eth0" | |
1787 | group_address = "225.1.1.1" | |
1788 | result = verify_pim_state(tgen, dut, iif, oil, group_address) | |
1789 | ||
1790 | Returns | |
1791 | ------- | |
1792 | errormsg(str) or True | |
1793 | """ | |
1794 | ||
1795 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1796 | ||
1797 | if dut not in tgen.routers(): | |
1798 | return False | |
1799 | ||
1800 | rnode = tgen.routers()[dut] | |
1801 | ||
1802 | logger.info("[DUT: %s]: Verifying pim state", dut) | |
e8cd26fd | 1803 | |
1804 | if type(group_addresses) is not list: | |
1805 | group_addresses = [group_addresses] | |
1806 | ||
e13f9c4f KK |
1807 | for grp in group_addresses: |
1808 | addr_type = validate_ip_address(grp) | |
1809 | ||
1810 | if addr_type == "ipv4": | |
1811 | ip_cmd = "ip" | |
1812 | elif addr_type == "ipv6": | |
1813 | ip_cmd = "ipv6" | |
1814 | ||
1815 | logger.info("[DUT: %s]: Verifying pim state", dut) | |
1816 | show_pim_state_json = run_frr_cmd( | |
1817 | rnode, "show {} pim state json".format(ip_cmd), isjson=True | |
1818 | ) | |
1819 | ||
1820 | if installed_fl is None: | |
1821 | installed_fl = 1 | |
1822 | ||
e8cd26fd | 1823 | for grp_addr in group_addresses: |
1824 | if src_address is None: | |
1825 | src_address = "*" | |
1826 | pim_state_json = show_pim_state_json[grp_addr][src_address] | |
1827 | else: | |
1828 | pim_state_json = show_pim_state_json[grp_addr][src_address] | |
1829 | ||
1830 | if pim_state_json["Installed"] == installed_fl: | |
1831 | logger.info( | |
1832 | "[DUT %s]: group %s is installed flag: %s", | |
1833 | dut, | |
1834 | grp_addr, | |
1835 | pim_state_json["Installed"], | |
1836 | ) | |
1837 | for interface, data in pim_state_json[iif].items(): | |
1838 | if interface != oil: | |
1839 | continue | |
1840 | ||
1841 | # Verify iif, oil and installed state | |
1842 | if ( | |
1843 | data["group"] == grp_addr | |
1844 | and data["installed"] == installed_fl | |
1845 | and data["inboundInterface"] == iif | |
1846 | and data["outboundInterface"] == oil | |
1847 | ): | |
1848 | logger.info( | |
1849 | "[DUT %s]: Verifying pim state for group" | |
1850 | " %s [PASSED]!! Found Expected: " | |
1851 | "(iif: %s, oil: %s, installed: %s) ", | |
1852 | dut, | |
1853 | grp_addr, | |
1854 | data["inboundInterface"], | |
1855 | data["outboundInterface"], | |
1856 | data["installed"], | |
1857 | ) | |
1858 | else: | |
1859 | errormsg = ( | |
1860 | "[DUT %s]: Verifying pim state for group" | |
1861 | " %s, [FAILED]!! Expected: " | |
5aab262b QY |
1862 | "(iif: %s, oil: %s, installed: %s) " |
1863 | % (dut, grp_addr, iif, oil, "1"), | |
e8cd26fd | 1864 | "Found: (iif: %s, oil: %s, installed: %s)" |
1865 | % ( | |
e8cd26fd | 1866 | data["inboundInterface"], |
1867 | data["outboundInterface"], | |
1868 | data["installed"], | |
1869 | ), | |
1870 | ) | |
1871 | return errormsg | |
1872 | else: | |
1873 | errormsg = "[DUT %s]: %s install flag value not as expected" % ( | |
1874 | dut, | |
1875 | grp_addr, | |
1876 | ) | |
1877 | return errormsg | |
1878 | ||
1879 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1880 | return True | |
1881 | ||
1882 | ||
0b01a0bb | 1883 | def get_pim_interface_traffic(tgen, input_dict): |
e8cd26fd | 1884 | """ |
d7032129 | 1885 | get ip pim interface traffic by running |
e8cd26fd | 1886 | "show ip pim interface traffic" cli |
1887 | ||
1888 | Parameters | |
1889 | ---------- | |
1890 | * `tgen`: topogen object | |
1891 | * `input_dict(dict)`: defines DUT, what and from which interfaces | |
0b01a0bb | 1892 | traffic needs to be retrieved |
e8cd26fd | 1893 | Usage |
1894 | ----- | |
1895 | input_dict = { | |
1896 | "r1": { | |
1897 | "r1-r0-eth0": { | |
1898 | "helloRx": 0, | |
1899 | "helloTx": 1, | |
1900 | "joinRx": 0, | |
1901 | "joinTx": 0 | |
1902 | } | |
1903 | } | |
1904 | } | |
1905 | ||
0b01a0bb | 1906 | result = get_pim_interface_traffic(tgen, input_dict) |
e8cd26fd | 1907 | |
1908 | Returns | |
1909 | ------- | |
1910 | errormsg(str) or True | |
1911 | """ | |
1912 | ||
1913 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1914 | ||
1915 | output_dict = {} | |
1916 | for dut in input_dict.keys(): | |
1917 | if dut not in tgen.routers(): | |
1918 | continue | |
1919 | ||
1920 | rnode = tgen.routers()[dut] | |
1921 | ||
1922 | logger.info("[DUT: %s]: Verifying pim interface traffic", dut) | |
e8cd26fd | 1923 | |
3d7d6e9a DS |
1924 | def show_pim_intf_traffic(rnode, dut, input_dict, output_dict): |
1925 | show_pim_intf_traffic_json = run_frr_cmd( | |
1926 | rnode, "show ip pim interface traffic json", isjson=True | |
1927 | ) | |
e8cd26fd | 1928 | |
3d7d6e9a DS |
1929 | output_dict[dut] = {} |
1930 | for intf, data in input_dict[dut].items(): | |
1931 | interface_json = show_pim_intf_traffic_json[intf] | |
1932 | for state in data: | |
1933 | ||
1934 | # Verify Tx/Rx | |
1935 | if state in interface_json: | |
1936 | output_dict[dut][state] = interface_json[state] | |
1937 | else: | |
1938 | errormsg = ( | |
1939 | "[DUT %s]: %s is not present" | |
1940 | "for interface %s [FAILED]!! " % (dut, state, intf) | |
1941 | ) | |
1942 | return errormsg | |
1943 | return None | |
1944 | ||
1945 | test_func = functools.partial( | |
1946 | show_pim_intf_traffic, rnode, dut, input_dict, output_dict | |
1947 | ) | |
1948 | (result, out) = topotest.run_and_expect(test_func, None, count=20, wait=1) | |
1949 | if not result: | |
1950 | return out | |
e8cd26fd | 1951 | |
1952 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1953 | return output_dict | |
1954 | ||
1955 | ||
d7032129 KK |
1956 | def get_pim6_interface_traffic(tgen, input_dict): |
1957 | """ | |
1958 | get ipv6 pim interface traffic by running | |
1959 | "show ipv6 pim interface traffic" cli | |
1960 | ||
1961 | Parameters | |
1962 | ---------- | |
1963 | * `tgen`: topogen object | |
1964 | * `input_dict(dict)`: defines DUT, what and from which interfaces | |
1965 | traffic needs to be retrieved | |
1966 | Usage | |
1967 | ----- | |
1968 | input_dict = { | |
1969 | "r1": { | |
1970 | "r1-r0-eth0": { | |
1971 | "helloRx": 0, | |
1972 | "helloTx": 1, | |
1973 | "joinRx": 0, | |
1974 | "joinTx": 0 | |
1975 | } | |
1976 | } | |
1977 | } | |
1978 | ||
1979 | result = get_pim_interface_traffic(tgen, input_dict) | |
1980 | ||
1981 | Returns | |
1982 | ------- | |
1983 | errormsg(str) or True | |
1984 | """ | |
1985 | ||
1986 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1987 | ||
1988 | output_dict = {} | |
1989 | for dut in input_dict.keys(): | |
1990 | if dut not in tgen.routers(): | |
1991 | continue | |
1992 | ||
1993 | rnode = tgen.routers()[dut] | |
1994 | ||
1995 | logger.info("[DUT: %s]: Verifying pim interface traffic", dut) | |
1996 | ||
1997 | def show_pim_intf_traffic(rnode, dut, input_dict, output_dict): | |
1998 | show_pim_intf_traffic_json = run_frr_cmd( | |
1999 | rnode, "show ipv6 pim interface traffic json", isjson=True | |
2000 | ) | |
2001 | ||
2002 | output_dict[dut] = {} | |
2003 | for intf, data in input_dict[dut].items(): | |
2004 | interface_json = show_pim_intf_traffic_json[intf] | |
2005 | for state in data: | |
2006 | ||
2007 | # Verify Tx/Rx | |
2008 | if state in interface_json: | |
2009 | output_dict[dut][state] = interface_json[state] | |
2010 | else: | |
2011 | errormsg = ( | |
2012 | "[DUT %s]: %s is not present" | |
2013 | "for interface %s [FAILED]!! " % (dut, state, intf) | |
2014 | ) | |
2015 | return errormsg | |
2016 | return None | |
2017 | ||
2018 | test_func = functools.partial( | |
2019 | show_pim_intf_traffic, rnode, dut, input_dict, output_dict | |
2020 | ) | |
2021 | (result, out) = topotest.run_and_expect(test_func, None, count=20, wait=1) | |
2022 | if not result: | |
2023 | return out | |
2024 | ||
2025 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2026 | return output_dict | |
2027 | ||
2028 | ||
de3d6b4d | 2029 | @retry(retry_timeout=40, diag_pct=0) |
a53c08bc | 2030 | def verify_pim_interface( |
d7032129 | 2031 | tgen, topo, dut, interface=None, interface_ip=None, addr_type="ipv4", expected=True |
a53c08bc | 2032 | ): |
e8cd26fd | 2033 | """ |
2034 | Verify all PIM interface are up and running, config is verified | |
2035 | using "show ip pim interface" cli | |
2036 | ||
2037 | Parameters | |
2038 | ---------- | |
2039 | * `tgen`: topogen object | |
2040 | * `topo` : json file data | |
2041 | * `dut` : device under test | |
eab72dc8 | 2042 | * `interface` : interface name |
2043 | * `interface_ip` : interface ip address | |
3c41ebf8 | 2044 | * `expected` : expected results from API, by-default True |
e8cd26fd | 2045 | |
2046 | Usage | |
2047 | ----- | |
eab72dc8 | 2048 | result = verify_pim_interfacetgen, topo, dut, interface=ens192, interface_ip=20.1.1.1) |
e8cd26fd | 2049 | |
2050 | Returns | |
2051 | ------- | |
2052 | errormsg(str) or True | |
2053 | """ | |
2054 | ||
2055 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2056 | ||
2057 | for router in tgen.routers(): | |
2058 | if router != dut: | |
2059 | continue | |
2060 | ||
2061 | logger.info("[DUT: %s]: Verifying PIM interface status:", dut) | |
2062 | ||
2063 | rnode = tgen.routers()[dut] | |
d7032129 KK |
2064 | |
2065 | if addr_type == "ipv4": | |
2066 | addr_cmd = "ip" | |
2067 | pim_cmd = "pim" | |
2068 | elif addr_type == "ipv6": | |
2069 | addr_cmd = "ipv6" | |
2070 | pim_cmd = "pim6" | |
2071 | show_pim_interface_json = rnode.vtysh_cmd( | |
2072 | "show {} pim interface json".format(addr_cmd), isjson=True | |
5980ad0a | 2073 | ) |
eab72dc8 | 2074 | |
d7032129 | 2075 | logger.info("show_pim_interface_json: \n %s", show_pim_interface_json) |
eab72dc8 | 2076 | |
2077 | if interface_ip: | |
d7032129 KK |
2078 | if interface in show_pim_interface_json: |
2079 | pim_intf_json = show_pim_interface_json[interface] | |
eab72dc8 | 2080 | if pim_intf_json["address"] != interface_ip: |
5980ad0a | 2081 | errormsg = ( |
d7032129 KK |
2082 | "[DUT %s]: %s interface " |
2083 | "%s is not correct " | |
5980ad0a | 2084 | "[FAILED]!! Expected : %s, Found : %s" |
d7032129 KK |
2085 | % ( |
2086 | dut, | |
2087 | pim_cmd, | |
2088 | addr_cmd, | |
2089 | pim_intf_json["address"], | |
2090 | interface_ip, | |
2091 | ) | |
5980ad0a | 2092 | ) |
eab72dc8 | 2093 | return errormsg |
2094 | else: | |
5980ad0a | 2095 | logger.info( |
d7032129 KK |
2096 | "[DUT %s]: %s interface " |
2097 | "%s is correct " | |
5980ad0a | 2098 | "[Passed]!! Expected : %s, Found : %s" |
d7032129 KK |
2099 | % ( |
2100 | dut, | |
2101 | pim_cmd, | |
2102 | addr_cmd, | |
2103 | pim_intf_json["address"], | |
2104 | interface_ip, | |
2105 | ) | |
5980ad0a | 2106 | ) |
eab72dc8 | 2107 | return True |
2108 | else: | |
2109 | for destLink, data in topo["routers"][dut]["links"].items(): | |
2110 | if "type" in data and data["type"] == "loopback": | |
2111 | continue | |
e8cd26fd | 2112 | |
d7032129 | 2113 | if pim_cmd in data and data[pim_cmd] == "enable": |
eab72dc8 | 2114 | pim_interface = data["interface"] |
d7032129 | 2115 | pim_intf_ip = data[addr_type].split("/")[0] |
e8cd26fd | 2116 | |
d7032129 KK |
2117 | if pim_interface in show_pim_interface_json: |
2118 | pim_intf_json = show_pim_interface_json[pim_interface] | |
e46ce55e KK |
2119 | else: |
2120 | errormsg = ( | |
d7032129 KK |
2121 | "[DUT %s]: %s interface: %s " |
2122 | "PIM interface %s: %s, not Found" | |
2123 | % (dut, pim_cmd, pim_interface, addr_cmd, pim_intf_ip) | |
e46ce55e KK |
2124 | ) |
2125 | return errormsg | |
e8cd26fd | 2126 | |
2127 | # Verifying PIM interface | |
5980ad0a DS |
2128 | if ( |
2129 | pim_intf_json["address"] != pim_intf_ip | |
2130 | and pim_intf_json["state"] != "up" | |
2131 | ): | |
2132 | errormsg = ( | |
d7032129 KK |
2133 | "[DUT %s]: %s interface: %s " |
2134 | "PIM interface %s: %s, status check " | |
5980ad0a DS |
2135 | "[FAILED]!! Expected : %s, Found : %s" |
2136 | % ( | |
2137 | dut, | |
d7032129 | 2138 | pim_cmd, |
5980ad0a | 2139 | pim_interface, |
d7032129 | 2140 | addr_cmd, |
5980ad0a DS |
2141 | pim_intf_ip, |
2142 | pim_interface, | |
2143 | pim_intf_json["state"], | |
2144 | ) | |
2145 | ) | |
e8cd26fd | 2146 | return errormsg |
2147 | ||
5980ad0a | 2148 | logger.info( |
d7032129 KK |
2149 | "[DUT %s]: %s interface: %s, " |
2150 | "interface %s: %s, status: %s" | |
5980ad0a DS |
2151 | " [PASSED]!!", |
2152 | dut, | |
d7032129 | 2153 | pim_cmd, |
5980ad0a | 2154 | pim_interface, |
d7032129 | 2155 | addr_cmd, |
5980ad0a DS |
2156 | pim_intf_ip, |
2157 | pim_intf_json["state"], | |
2158 | ) | |
e8cd26fd | 2159 | |
2160 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2161 | return True | |
2162 | ||
2163 | ||
4fafd29f | 2164 | def clear_pim_interface_traffic(tgen, topo): |
e8cd26fd | 2165 | """ |
d7032129 KK |
2166 | Clear ip pim interface traffic by running |
2167 | "clear ip pim interface traffic" cli | |
e8cd26fd | 2168 | |
2169 | Parameters | |
2170 | ---------- | |
2171 | * `tgen`: topogen object | |
2172 | Usage | |
2173 | ----- | |
2174 | ||
4fafd29f | 2175 | result = clear_pim_interface_traffic(tgen, topo) |
e8cd26fd | 2176 | |
2177 | Returns | |
2178 | ------- | |
2179 | errormsg(str) or True | |
2180 | """ | |
2181 | ||
2182 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2183 | ||
2184 | for dut in tgen.routers(): | |
2185 | if "pim" not in topo["routers"][dut]: | |
2186 | continue | |
2187 | ||
2188 | rnode = tgen.routers()[dut] | |
2189 | ||
2190 | logger.info("[DUT: %s]: Clearing pim interface traffic", dut) | |
2191 | result = run_frr_cmd(rnode, "clear ip pim interface traffic") | |
2192 | ||
2193 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2194 | ||
2195 | return True | |
2196 | ||
2197 | ||
d7032129 | 2198 | def clear_pim6_interface_traffic(tgen, topo): |
e8cd26fd | 2199 | """ |
d7032129 KK |
2200 | Clear ipv6 pim interface traffic by running |
2201 | "clear ipv6 pim interface traffic" cli | |
e8cd26fd | 2202 | |
2203 | Parameters | |
2204 | ---------- | |
2205 | * `tgen`: topogen object | |
e8cd26fd | 2206 | Usage |
2207 | ----- | |
2208 | ||
d7032129 | 2209 | result = clear_pim6_interface_traffic(tgen, topo) |
e8cd26fd | 2210 | |
2211 | Returns | |
2212 | ------- | |
2213 | errormsg(str) or True | |
2214 | """ | |
2215 | ||
2216 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2217 | ||
d7032129 KK |
2218 | for dut in tgen.routers(): |
2219 | if "pim" not in topo["routers"][dut]: | |
2220 | continue | |
e8cd26fd | 2221 | |
d7032129 | 2222 | rnode = tgen.routers()[dut] |
e8cd26fd | 2223 | |
d7032129 KK |
2224 | logger.info("[DUT: %s]: Clearing pim6 interface traffic", dut) |
2225 | result = run_frr_cmd(rnode, "clear ipv6 pim interface traffic") | |
e8cd26fd | 2226 | |
d7032129 | 2227 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) |
e8cd26fd | 2228 | |
d7032129 | 2229 | return True |
e8cd26fd | 2230 | |
e8cd26fd | 2231 | |
d7032129 KK |
2232 | def clear_pim6_interfaces(tgen, topo): |
2233 | """ | |
2234 | Clear ipv6 pim interface by running | |
2235 | "clear ipv6 pim interface" cli | |
e8cd26fd | 2236 | |
d7032129 KK |
2237 | Parameters |
2238 | ---------- | |
2239 | * `tgen`: topogen object | |
2240 | Usage | |
2241 | ----- | |
2242 | ||
2243 | result = clear_pim6_interfaces(tgen, topo) | |
2244 | ||
2245 | Returns | |
2246 | ------- | |
2247 | errormsg(str) or True | |
2248 | """ | |
2249 | ||
2250 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2251 | ||
2252 | for dut in tgen.routers(): | |
2253 | if "pim" not in topo["routers"][dut]: | |
2254 | continue | |
2255 | ||
2256 | rnode = tgen.routers()[dut] | |
2257 | ||
2258 | logger.info("[DUT: %s]: Clearing pim6 interfaces", dut) | |
2259 | result = run_frr_cmd(rnode, "clear ipv6 pim interface") | |
2260 | ||
2261 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2262 | ||
2263 | return True | |
2264 | ||
2265 | ||
2266 | def clear_pim_interfaces(tgen, dut): | |
2267 | """ | |
2268 | Clear ip/ipv6 pim interface by running | |
2269 | "clear ip/ipv6 pim interfaces" cli | |
2270 | ||
2271 | Parameters | |
2272 | ---------- | |
2273 | * `tgen`: topogen object | |
2274 | * `dut`: Device Under Test | |
2275 | Usage | |
2276 | ----- | |
2277 | ||
2278 | result = clear_pim_interfaces(tgen, dut) | |
2279 | ||
2280 | Returns | |
2281 | ------- | |
2282 | errormsg(str) or True | |
2283 | """ | |
2284 | ||
2285 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2286 | ||
2287 | nh_before_clear = {} | |
2288 | nh_after_clear = {} | |
2289 | ||
2290 | rnode = tgen.routers()[dut] | |
2291 | ||
2292 | logger.info("[DUT: %s]: Verify pim neighbor before pim" " neighbor clear", dut) | |
2293 | # To add uptime initially | |
2294 | sleep(10) | |
2295 | run_json_before = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True) | |
2296 | ||
2297 | for key, value in run_json_before.items(): | |
2298 | if bool(value): | |
2299 | for _key, _value in value.items(): | |
2300 | nh_before_clear[key] = _value["upTime"] | |
2301 | ||
2302 | # Clearing PIM neighbors | |
2303 | logger.info("[DUT: %s]: Clearing pim interfaces", dut) | |
2304 | run_frr_cmd(rnode, "clear ip pim interfaces") | |
2305 | ||
2306 | logger.info("[DUT: %s]: Verify pim neighbor after pim" " neighbor clear", dut) | |
2307 | ||
2308 | found = False | |
2309 | ||
2310 | # Waiting for maximum 60 sec | |
2311 | fail_intf = [] | |
2312 | for retry in range(1, 13): | |
2313 | sleep(5) | |
2314 | logger.info("[DUT: %s]: Waiting for 5 sec for PIM neighbors" " to come up", dut) | |
2315 | run_json_after = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True) | |
2316 | found = True | |
2317 | for pim_intf in nh_before_clear.keys(): | |
2318 | if pim_intf not in run_json_after or not run_json_after[pim_intf]: | |
2319 | found = False | |
e8cd26fd | 2320 | fail_intf.append(pim_intf) |
2321 | ||
2322 | if found is True: | |
2323 | break | |
2324 | else: | |
2325 | errormsg = ( | |
2326 | "[DUT: %s]: pim neighborship is not formed for %s" | |
2327 | "after clear_ip_pim_interfaces %s [FAILED!!]", | |
2328 | dut, | |
2329 | fail_intf, | |
2330 | ) | |
2331 | return errormsg | |
2332 | ||
2333 | for key, value in run_json_after.items(): | |
2334 | if bool(value): | |
2335 | for _key, _value in value.items(): | |
2336 | nh_after_clear[key] = _value["upTime"] | |
2337 | ||
2338 | # Verify uptime for neighbors | |
2339 | for pim_intf in nh_before_clear.keys(): | |
2340 | d1 = datetime.datetime.strptime(nh_before_clear[pim_intf], "%H:%M:%S") | |
2341 | d2 = datetime.datetime.strptime(nh_after_clear[pim_intf], "%H:%M:%S") | |
2342 | if d2 >= d1: | |
2343 | errormsg = ( | |
2344 | "[DUT: %s]: PIM neighborship is not cleared for", | |
2345 | " interface %s [FAILED!!]", | |
2346 | dut, | |
2347 | pim_intf, | |
2348 | ) | |
2349 | ||
2350 | logger.info("[DUT: %s]: PIM neighborship is cleared [PASSED!!]") | |
2351 | ||
2352 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2353 | ||
2354 | return True | |
2355 | ||
2356 | ||
4fafd29f | 2357 | def clear_igmp_interfaces(tgen, dut): |
e8cd26fd | 2358 | """ |
4fafd29f KK |
2359 | Clear ip/ipv6 igmp interfaces by running |
2360 | "clear ip/ipv6 igmp interfaces" cli | |
e8cd26fd | 2361 | |
2362 | Parameters | |
2363 | ---------- | |
2364 | * `tgen`: topogen object | |
2365 | * `dut`: device under test | |
2366 | ||
2367 | Usage | |
2368 | ----- | |
2369 | dut = "r1" | |
4fafd29f | 2370 | result = clear_igmp_interfaces(tgen, dut) |
e8cd26fd | 2371 | Returns |
2372 | ------- | |
2373 | errormsg(str) or True | |
2374 | """ | |
2375 | ||
2376 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2377 | ||
2378 | group_before_clear = {} | |
2379 | group_after_clear = {} | |
2380 | ||
2381 | rnode = tgen.routers()[dut] | |
2382 | ||
2383 | logger.info("[DUT: %s]: IGMP group uptime before clear" " igmp groups:", dut) | |
2384 | igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True) | |
2385 | ||
2386 | total_groups_before_clear = igmp_json["totalGroups"] | |
2387 | ||
2388 | for key, value in igmp_json.items(): | |
2389 | if type(value) is not dict: | |
2390 | continue | |
2391 | ||
2392 | groups = value["groups"] | |
2393 | group = groups[0]["group"] | |
2394 | uptime = groups[0]["uptime"] | |
2395 | group_before_clear[group] = uptime | |
2396 | ||
2397 | logger.info("[DUT: %s]: Clearing ip igmp interfaces", dut) | |
2398 | result = run_frr_cmd(rnode, "clear ip igmp interfaces") | |
2399 | ||
2400 | # Waiting for maximum 60 sec | |
2401 | for retry in range(1, 13): | |
2402 | logger.info( | |
2403 | "[DUT: %s]: Waiting for 5 sec for igmp interfaces" " to come up", dut | |
2404 | ) | |
2405 | sleep(5) | |
2406 | igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True) | |
2407 | ||
2408 | total_groups_after_clear = igmp_json["totalGroups"] | |
2409 | ||
2410 | if total_groups_before_clear == total_groups_after_clear: | |
2411 | break | |
2412 | ||
2413 | for key, value in igmp_json.items(): | |
2414 | if type(value) is not dict: | |
2415 | continue | |
2416 | ||
2417 | groups = value["groups"] | |
2418 | group = groups[0]["group"] | |
2419 | uptime = groups[0]["uptime"] | |
2420 | group_after_clear[group] = uptime | |
2421 | ||
2422 | # Verify uptime for groups | |
2423 | for group in group_before_clear.keys(): | |
2424 | d1 = datetime.datetime.strptime(group_before_clear[group], "%H:%M:%S") | |
2425 | d2 = datetime.datetime.strptime(group_after_clear[group], "%H:%M:%S") | |
2426 | if d2 >= d1: | |
2427 | errormsg = ("[DUT: %s]: IGMP group is not cleared", " [FAILED!!]", dut) | |
2428 | ||
2429 | logger.info("[DUT: %s]: IGMP group is cleared [PASSED!!]") | |
2430 | ||
2431 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2432 | ||
2433 | return True | |
2434 | ||
2435 | ||
ed776e38 | 2436 | @retry(retry_timeout=20) |
4fafd29f | 2437 | def clear_mroute_verify(tgen, dut, expected=True): |
e8cd26fd | 2438 | """ |
4fafd29f | 2439 | Clear ip/ipv6 mroute by running "clear ip/ipv6 mroute" cli and verify |
e8cd26fd | 2440 | mroutes are up again after mroute clear |
2441 | ||
2442 | Parameters | |
2443 | ---------- | |
2444 | * `tgen`: topogen object | |
2445 | * `dut`: Device Under Test | |
3c41ebf8 KK |
2446 | * `expected` : expected results from API, by-default True |
2447 | ||
e8cd26fd | 2448 | Usage |
2449 | ----- | |
2450 | ||
4fafd29f | 2451 | result = clear_mroute_verify(tgen, dut) |
e8cd26fd | 2452 | |
2453 | Returns | |
2454 | ------- | |
2455 | errormsg(str) or True | |
2456 | """ | |
2457 | ||
2458 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2459 | ||
2460 | mroute_before_clear = {} | |
2461 | mroute_after_clear = {} | |
2462 | ||
2463 | rnode = tgen.routers()[dut] | |
2464 | ||
e8cd26fd | 2465 | logger.info("[DUT: %s]: IP mroutes uptime before clear", dut) |
2466 | mroute_json_1 = run_frr_cmd(rnode, "show ip mroute json", isjson=True) | |
2467 | ||
2468 | for group in mroute_json_1.keys(): | |
2469 | mroute_before_clear[group] = {} | |
2470 | for key in mroute_json_1[group].keys(): | |
2471 | for _key, _value in mroute_json_1[group][key]["oil"].items(): | |
2472 | if _key != "pimreg": | |
2473 | mroute_before_clear[group][key] = _value["upTime"] | |
2474 | ||
2475 | logger.info("[DUT: %s]: Clearing ip mroute", dut) | |
2476 | result = run_frr_cmd(rnode, "clear ip mroute") | |
2477 | ||
2478 | # RFC 3376: 8.2. Query Interval - Default: 125 seconds | |
2479 | # So waiting for maximum 130 sec to get the igmp report | |
2480 | for retry in range(1, 26): | |
2481 | logger.info("[DUT: %s]: Waiting for 2 sec for mroutes" " to come up", dut) | |
2482 | sleep(5) | |
2483 | keys_json1 = mroute_json_1.keys() | |
2484 | mroute_json_2 = run_frr_cmd(rnode, "show ip mroute json", isjson=True) | |
2485 | ||
2486 | if bool(mroute_json_2): | |
2487 | keys_json2 = mroute_json_2.keys() | |
2488 | ||
2489 | for group in mroute_json_2.keys(): | |
2490 | flag = False | |
2491 | for key in mroute_json_2[group].keys(): | |
2492 | if "oil" not in mroute_json_2[group]: | |
2493 | continue | |
2494 | ||
2495 | for _key, _value in mroute_json_2[group][key]["oil"].items(): | |
2496 | if _key != "pimreg" and keys_json1 == keys_json2: | |
2497 | break | |
2498 | flag = True | |
2499 | if flag: | |
2500 | break | |
2501 | else: | |
2502 | continue | |
2503 | ||
2504 | for group in mroute_json_2.keys(): | |
2505 | mroute_after_clear[group] = {} | |
2506 | for key in mroute_json_2[group].keys(): | |
2507 | for _key, _value in mroute_json_2[group][key]["oil"].items(): | |
2508 | if _key != "pimreg": | |
2509 | mroute_after_clear[group][key] = _value["upTime"] | |
2510 | ||
2511 | # Verify uptime for mroute | |
2512 | for group in mroute_before_clear.keys(): | |
2513 | for source in mroute_before_clear[group].keys(): | |
2514 | if set(mroute_before_clear[group]) != set(mroute_after_clear[group]): | |
2515 | errormsg = ( | |
2516 | "[DUT: %s]: mroute (%s, %s) has not come" | |
2517 | " up after mroute clear [FAILED!!]" % (dut, source, group) | |
2518 | ) | |
2519 | return errormsg | |
2520 | ||
2521 | d1 = datetime.datetime.strptime( | |
2522 | mroute_before_clear[group][source], "%H:%M:%S" | |
2523 | ) | |
2524 | d2 = datetime.datetime.strptime( | |
2525 | mroute_after_clear[group][source], "%H:%M:%S" | |
2526 | ) | |
2527 | if d2 >= d1: | |
2528 | errormsg = "[DUT: %s]: IP mroute is not cleared" " [FAILED!!]" % (dut) | |
2529 | ||
2530 | logger.info("[DUT: %s]: IP mroute is cleared [PASSED!!]", dut) | |
2531 | ||
2532 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2533 | ||
2534 | return True | |
2535 | ||
2536 | ||
4fafd29f | 2537 | def clear_mroute(tgen, dut=None): |
e8cd26fd | 2538 | """ |
4fafd29f | 2539 | Clear ip/ipv6 mroute by running "clear ip mroute" cli |
e8cd26fd | 2540 | |
2541 | Parameters | |
2542 | ---------- | |
2543 | * `tgen`: topogen object | |
2544 | * `dut`: device under test, default None | |
2545 | ||
2546 | Usage | |
2547 | ----- | |
4fafd29f | 2548 | clear_mroute(tgen, dut) |
e8cd26fd | 2549 | """ |
2550 | ||
2551 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2552 | ||
2553 | router_list = tgen.routers() | |
2554 | for router, rnode in router_list.items(): | |
2555 | if dut is not None and router != dut: | |
2556 | continue | |
2557 | ||
2558 | logger.debug("[DUT: %s]: Clearing ip mroute", router) | |
2559 | rnode.vtysh_cmd("clear ip mroute") | |
2560 | ||
2561 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2562 | ||
2563 | ||
d7032129 KK |
2564 | def clear_pim6_mroute(tgen, dut=None): |
2565 | """ | |
2566 | Clear ipv6 mroute by running "clear ipv6 mroute" cli | |
2567 | ||
2568 | Parameters | |
2569 | ---------- | |
2570 | * `tgen`: topogen object | |
2571 | * `dut`: device under test, default None | |
2572 | ||
2573 | Usage | |
2574 | ----- | |
2575 | clear_mroute(tgen, dut) | |
2576 | """ | |
2577 | ||
2578 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2579 | ||
2580 | router_list = tgen.routers() | |
2581 | for router, rnode in router_list.items(): | |
2582 | if dut is not None and router != dut: | |
2583 | continue | |
2584 | ||
2585 | logger.debug("[DUT: %s]: Clearing ipv6 mroute", router) | |
2586 | rnode.vtysh_cmd("clear ipv6 mroute") | |
2587 | ||
2588 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2589 | ||
2590 | return True | |
2591 | ||
2592 | ||
e8cd26fd | 2593 | def reconfig_interfaces(tgen, topo, senderRouter, receiverRouter, packet=None): |
2594 | """ | |
2595 | Configure interface ip for sender and receiver routers | |
2596 | as per bsr packet | |
2597 | ||
2598 | Parameters | |
2599 | ---------- | |
2600 | * `tgen` : Topogen object | |
2601 | * `topo` : json file data | |
2602 | * `senderRouter` : Sender router | |
2603 | * `receiverRouter` : Receiver router | |
2604 | * `packet` : BSR packet in raw format | |
2605 | ||
2606 | Returns | |
2607 | ------- | |
2608 | True or False | |
2609 | """ | |
2610 | result = False | |
2611 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2612 | ||
2613 | try: | |
2614 | config_data = [] | |
2615 | ||
2616 | src_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["src_ip"] | |
2617 | dest_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["dest_ip"] | |
2618 | ||
2619 | for destLink, data in topo["routers"][senderRouter]["links"].items(): | |
2620 | if "type" in data and data["type"] == "loopback": | |
2621 | continue | |
2622 | ||
2623 | if "pim" in data and data["pim"] == "enable": | |
2624 | sender_interface = data["interface"] | |
2625 | sender_interface_ip = data["ipv4"] | |
2626 | ||
2627 | config_data.append("interface {}".format(sender_interface)) | |
2628 | config_data.append("no ip address {}".format(sender_interface_ip)) | |
2629 | config_data.append("ip address {}".format(src_ip)) | |
2630 | ||
2631 | result = create_common_configuration( | |
2632 | tgen, senderRouter, config_data, "interface_config" | |
2633 | ) | |
2634 | if result is not True: | |
2635 | return False | |
2636 | ||
2637 | config_data = [] | |
2638 | links = topo["routers"][destLink]["links"] | |
2639 | pim_neighbor = {key: links[key] for key in [senderRouter]} | |
2640 | ||
2641 | data = pim_neighbor[senderRouter] | |
2642 | if "type" in data and data["type"] == "loopback": | |
2643 | continue | |
2644 | ||
2645 | if "pim" in data and data["pim"] == "enable": | |
2646 | receiver_interface = data["interface"] | |
2647 | receiver_interface_ip = data["ipv4"] | |
2648 | ||
2649 | config_data.append("interface {}".format(receiver_interface)) | |
2650 | config_data.append("no ip address {}".format(receiver_interface_ip)) | |
2651 | config_data.append("ip address {}".format(dest_ip)) | |
2652 | ||
2653 | result = create_common_configuration( | |
2654 | tgen, receiverRouter, config_data, "interface_config" | |
2655 | ) | |
2656 | if result is not True: | |
2657 | return False | |
2658 | ||
2659 | except InvalidCLIError: | |
2660 | # Traceback | |
2661 | errormsg = traceback.format_exc() | |
2662 | logger.error(errormsg) | |
2663 | return errormsg | |
2664 | ||
2665 | logger.debug("Exiting lib API: reconfig_interfaces()") | |
2666 | return result | |
2667 | ||
2668 | ||
2669 | def add_rp_interfaces_and_pim_config(tgen, topo, interface, rp, rp_mapping): | |
2670 | """ | |
2671 | Add physical interfaces tp RP for all the RPs | |
2672 | ||
2673 | Parameters | |
2674 | ---------- | |
2675 | * `tgen` : Topogen object | |
2676 | * `topo` : json file data | |
2677 | * `interface` : RP interface | |
2678 | * `rp` : rp for given topology | |
2679 | * `rp_mapping` : dictionary of all groups and RPs | |
2680 | ||
2681 | Returns | |
2682 | ------- | |
2683 | True or False | |
2684 | """ | |
2685 | result = False | |
2686 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2687 | ||
2688 | try: | |
2689 | config_data = [] | |
2690 | ||
2691 | for group, rp_list in rp_mapping.items(): | |
2692 | for _rp in rp_list: | |
2693 | config_data.append("interface {}".format(interface)) | |
2694 | config_data.append("ip address {}".format(_rp)) | |
2695 | config_data.append("ip pim") | |
2696 | ||
4f99894d | 2697 | # Why not config just once, why per group? |
e8cd26fd | 2698 | result = create_common_configuration( |
2699 | tgen, rp, config_data, "interface_config" | |
2700 | ) | |
2701 | if result is not True: | |
2702 | return False | |
2703 | ||
2704 | except InvalidCLIError: | |
2705 | # Traceback | |
2706 | errormsg = traceback.format_exc() | |
2707 | logger.error(errormsg) | |
2708 | return errormsg | |
2709 | ||
8ab46256 | 2710 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) |
e8cd26fd | 2711 | return result |
2712 | ||
2713 | ||
49581587 | 2714 | def scapy_send_bsr_raw_packet(tgen, topo, senderRouter, receiverRouter, packet=None): |
e8cd26fd | 2715 | """ |
2716 | Using scapy Raw() method to send BSR raw packet from one FRR | |
2717 | to other | |
2718 | ||
2719 | Parameters: | |
2720 | ----------- | |
2721 | * `tgen` : Topogen object | |
2722 | * `topo` : json file data | |
2723 | * `senderRouter` : Sender router | |
2724 | * `receiverRouter` : Receiver router | |
2725 | * `packet` : BSR packet in raw format | |
e8cd26fd | 2726 | |
2727 | returns: | |
2728 | -------- | |
2729 | errormsg or True | |
2730 | """ | |
2731 | ||
2732 | global CWD | |
2733 | result = "" | |
2734 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2735 | ||
49581587 CH |
2736 | python3_path = tgen.net.get_exec_path(["python3", "python"]) |
2737 | script_path = os.path.join(CWD, "send_bsr_packet.py") | |
2738 | node = tgen.net[senderRouter] | |
e8cd26fd | 2739 | |
2740 | for destLink, data in topo["routers"][senderRouter]["links"].items(): | |
2741 | if "type" in data and data["type"] == "loopback": | |
2742 | continue | |
2743 | ||
2744 | if "pim" in data and data["pim"] == "enable": | |
2745 | sender_interface = data["interface"] | |
2746 | ||
2747 | packet = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["data"] | |
2748 | ||
49581587 CH |
2749 | cmd = [ |
2750 | python3_path, | |
2751 | script_path, | |
2752 | packet, | |
2753 | sender_interface, | |
2754 | "--interval=1", | |
2755 | "--count=1", | |
2756 | ] | |
e8cd26fd | 2757 | logger.info("Scapy cmd: \n %s", cmd) |
49581587 | 2758 | node.cmd_raises(cmd) |
e8cd26fd | 2759 | |
2760 | logger.debug("Exiting lib API: scapy_send_bsr_raw_packet") | |
2761 | return True | |
2762 | ||
2763 | ||
2764 | def find_rp_from_bsrp_info(tgen, dut, bsr, grp=None): | |
2765 | """ | |
2766 | Find which RP is having lowest prioriy and returns rp IP | |
2767 | ||
2768 | Parameters | |
2769 | ---------- | |
2770 | * `tgen`: topogen object | |
2771 | * `dut`: device under test | |
2772 | * `bsr`: BSR address | |
2773 | * 'grp': Group Address | |
2774 | ||
2775 | Usage | |
2776 | ----- | |
2777 | dut = "r1" | |
2778 | result = verify_pim_rp_info(tgen, dut, bsr) | |
2779 | ||
2780 | Returns: | |
2781 | dictionary: group and RP, which has to be installed as per | |
2782 | lowest priority or highest priority | |
2783 | """ | |
2784 | ||
2785 | rp_details = {} | |
2786 | rnode = tgen.routers()[dut] | |
2787 | ||
2788 | logger.info("[DUT: %s]: Fetching rp details from bsrp-info", dut) | |
2789 | bsrp_json = run_frr_cmd(rnode, "show ip pim bsrp-info json", isjson=True) | |
2790 | ||
2791 | if grp not in bsrp_json: | |
2792 | return {} | |
2793 | ||
2794 | for group, rp_data in bsrp_json.items(): | |
2795 | if group == "BSR Address" and bsrp_json["BSR Address"] == bsr: | |
2796 | continue | |
2797 | ||
2798 | if group != grp: | |
2799 | continue | |
2800 | ||
2801 | rp_priority = {} | |
2802 | rp_hash = {} | |
2803 | ||
2804 | for rp, value in rp_data.items(): | |
2805 | if rp == "Pending RP count": | |
2806 | continue | |
2807 | rp_priority[value["Rp Address"]] = value["Rp Priority"] | |
2808 | rp_hash[value["Rp Address"]] = value["Hash Val"] | |
2809 | ||
2810 | priority_dict = dict(zip(rp_priority.values(), rp_priority.keys())) | |
2811 | hash_dict = dict(zip(rp_hash.values(), rp_hash.keys())) | |
2812 | ||
2813 | # RP with lowest priority | |
2814 | if len(priority_dict) != 1: | |
2815 | rp_p, lowest_priority = sorted(rp_priority.items(), key=lambda x: x[1])[0] | |
2816 | rp_details[group] = rp_p | |
2817 | ||
2818 | # RP with highest hash value | |
2819 | if len(priority_dict) == 1: | |
2820 | rp_h, highest_hash = sorted(rp_hash.items(), key=lambda x: x[1])[-1] | |
2821 | rp_details[group] = rp_h | |
2822 | ||
2823 | # RP with highest IP address | |
2824 | if len(priority_dict) == 1 and len(hash_dict) == 1: | |
2825 | rp_details[group] = sorted(rp_priority.keys())[-1] | |
2826 | ||
2827 | return rp_details | |
2828 | ||
2829 | ||
ed776e38 | 2830 | @retry(retry_timeout=12) |
a53c08bc CH |
2831 | def verify_pim_grp_rp_source( |
2832 | tgen, topo, dut, grp_addr, rp_source, rpadd=None, expected=True | |
2833 | ): | |
e8cd26fd | 2834 | """ |
2835 | Verify pim rp info by running "show ip pim rp-info" cli | |
2836 | ||
2837 | Parameters | |
2838 | ---------- | |
2839 | * `tgen`: topogen object | |
2840 | * `topo`: JSON file handler | |
2841 | * `dut`: device under test | |
2842 | * `grp_addr`: IGMP group address | |
2843 | * 'rp_source': source from which rp installed | |
2844 | * 'rpadd': rp address | |
3c41ebf8 | 2845 | * `expected` : expected results from API, by-default True |
e8cd26fd | 2846 | |
2847 | Usage | |
2848 | ----- | |
2849 | dut = "r1" | |
2850 | group_address = "225.1.1.1" | |
2851 | rp_source = "BSR" | |
2852 | result = verify_pim_rp_and_source(tgen, topo, dut, group_address, rp_source) | |
2853 | ||
2854 | Returns | |
2855 | ------- | |
2856 | errormsg(str) or True | |
2857 | """ | |
2858 | ||
2859 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2860 | ||
2861 | if dut not in tgen.routers(): | |
2862 | return False | |
2863 | ||
2864 | rnode = tgen.routers()[dut] | |
2865 | ||
2866 | logger.info("[DUT: %s]: Verifying ip rp info", dut) | |
2867 | show_ip_rp_info_json = run_frr_cmd(rnode, "show ip pim rp-info json", isjson=True) | |
2868 | ||
2869 | if rpadd != None: | |
2870 | rp_json = show_ip_rp_info_json[rpadd] | |
2871 | if rp_json[0]["group"] == grp_addr: | |
2872 | if rp_json[0]["source"] == rp_source: | |
2873 | logger.info( | |
2874 | "[DUT %s]: Verifying Group and rp_source [PASSED]" | |
2875 | "Found Expected: %s, %s" | |
2876 | % (dut, rp_json[0]["group"], rp_json[0]["source"]) | |
2877 | ) | |
2878 | return True | |
2879 | else: | |
2880 | errormsg = ( | |
2881 | "[DUT %s]: Verifying Group and rp_source [FAILED]" | |
2882 | "Expected (%s, %s) " | |
2883 | "Found (%s, %s)" | |
2884 | % ( | |
2885 | dut, | |
2886 | grp_addr, | |
2887 | rp_source, | |
2888 | rp_json[0]["group"], | |
2889 | rp_json[0]["source"], | |
2890 | ) | |
2891 | ) | |
2892 | return errormsg | |
2893 | errormsg = ( | |
2894 | "[DUT %s]: Verifying Group and rp_source [FAILED]" | |
2895 | "Expected: %s, %s but not found" % (dut, grp_addr, rp_source) | |
2896 | ) | |
2897 | return errormsg | |
2898 | ||
2899 | for rp in show_ip_rp_info_json: | |
2900 | rp_json = show_ip_rp_info_json[rp] | |
2901 | logger.info("%s", rp_json) | |
2902 | if rp_json[0]["group"] == grp_addr: | |
2903 | if rp_json[0]["source"] == rp_source: | |
2904 | logger.info( | |
2905 | "[DUT %s]: Verifying Group and rp_source [PASSED]" | |
2906 | "Found Expected: %s, %s" | |
2907 | % (dut, rp_json[0]["group"], rp_json[0]["source"]) | |
2908 | ) | |
2909 | return True | |
2910 | else: | |
2911 | errormsg = ( | |
2912 | "[DUT %s]: Verifying Group and rp_source [FAILED]" | |
2913 | "Expected (%s, %s) " | |
2914 | "Found (%s, %s)" | |
2915 | % ( | |
2916 | dut, | |
2917 | grp_addr, | |
2918 | rp_source, | |
2919 | rp_json[0]["group"], | |
2920 | rp_json[0]["source"], | |
2921 | ) | |
2922 | ) | |
2923 | return errormsg | |
2924 | ||
2925 | errormsg = ( | |
2926 | "[DUT %s]: Verifying Group and rp_source [FAILED]" | |
2927 | "Expected: %s, %s but not found" % (dut, grp_addr, rp_source) | |
2928 | ) | |
2929 | ||
2930 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2931 | ||
2932 | return errormsg | |
2933 | ||
2934 | ||
de3d6b4d | 2935 | @retry(retry_timeout=60, diag_pct=0) |
3c41ebf8 | 2936 | def verify_pim_bsr(tgen, topo, dut, bsr_ip, expected=True): |
e8cd26fd | 2937 | """ |
2938 | Verify all PIM interface are up and running, config is verified | |
2939 | using "show ip pim interface" cli | |
2940 | ||
2941 | Parameters | |
2942 | ---------- | |
2943 | * `tgen`: topogen object | |
2944 | * `topo` : json file data | |
2945 | * `dut` : device under test | |
2946 | * 'bsr' : bsr ip to be verified | |
3c41ebf8 | 2947 | * `expected` : expected results from API, by-default True |
e8cd26fd | 2948 | |
2949 | Usage | |
2950 | ----- | |
2951 | result = verify_pim_bsr(tgen, topo, dut, bsr_ip) | |
2952 | ||
2953 | Returns | |
2954 | ------- | |
2955 | errormsg(str) or True | |
2956 | """ | |
2957 | ||
2958 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2959 | ||
2960 | for router in tgen.routers(): | |
2961 | if router != dut: | |
2962 | continue | |
2963 | ||
2964 | logger.info("[DUT: %s]: Verifying PIM bsr status:", dut) | |
2965 | ||
2966 | rnode = tgen.routers()[dut] | |
2967 | pim_bsr_json = rnode.vtysh_cmd("show ip pim bsr json", isjson=True) | |
2968 | ||
2969 | logger.info("show_ip_pim_bsr_json: \n %s", pim_bsr_json) | |
2970 | ||
2971 | # Verifying PIM bsr | |
2972 | if pim_bsr_json["bsr"] != bsr_ip: | |
2973 | errormsg = ( | |
2974 | "[DUT %s]:" | |
2975 | "bsr status: not found" | |
2976 | "[FAILED]!! Expected : %s, Found : %s" | |
2977 | % (dut, bsr_ip, pim_bsr_json["bsr"]) | |
2978 | ) | |
2979 | return errormsg | |
2980 | ||
2981 | logger.info( | |
2982 | "[DUT %s]:" " bsr status: found, Address :%s" " [PASSED]!!", | |
2983 | dut, | |
2984 | pim_bsr_json["bsr"], | |
2985 | ) | |
2986 | ||
2987 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2988 | return True | |
2989 | ||
2990 | ||
de3d6b4d | 2991 | @retry(retry_timeout=60, diag_pct=0) |
4fafd29f | 2992 | def verify_pim_upstream_rpf( |
a53c08bc CH |
2993 | tgen, topo, dut, interface, group_addresses, rp=None, expected=True |
2994 | ): | |
e8cd26fd | 2995 | """ |
4fafd29f KK |
2996 | Verify IP/IPv6 PIM upstream rpf, config is verified |
2997 | using "show ip/ipv6 pim neighbor" cli | |
e8cd26fd | 2998 | |
2999 | Parameters | |
3000 | ---------- | |
3001 | * `tgen`: topogen object | |
3002 | * `topo` : json file data | |
3003 | * `dut` : devuce under test | |
3004 | * `interface` : upstream interface | |
3005 | * `group_addresses` : list of group address for which upstream info | |
3006 | needs to be checked | |
3007 | * `rp` : RP address | |
3c41ebf8 | 3008 | * `expected` : expected results from API, by-default True |
e8cd26fd | 3009 | |
3010 | Usage | |
3011 | ----- | |
4fafd29f | 3012 | result = verify_pim_upstream_rpf(gen, topo, dut, interface, |
e8cd26fd | 3013 | group_addresses, rp=None) |
3014 | ||
3015 | Returns | |
3016 | ------- | |
3017 | errormsg(str) or True | |
3018 | """ | |
3019 | ||
3020 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3021 | ||
3022 | if "pim" in topo["routers"][dut]: | |
3023 | ||
3024 | logger.info("[DUT: %s]: Verifying ip pim upstream rpf:", dut) | |
3025 | ||
3026 | rnode = tgen.routers()[dut] | |
3027 | show_ip_pim_upstream_rpf_json = rnode.vtysh_cmd( | |
3028 | "show ip pim upstream-rpf json", isjson=True | |
3029 | ) | |
3030 | ||
3031 | logger.info( | |
3032 | "show_ip_pim_upstream_rpf_json: \n %s", show_ip_pim_upstream_rpf_json | |
3033 | ) | |
3034 | ||
3035 | if type(group_addresses) is not list: | |
3036 | group_addresses = [group_addresses] | |
3037 | ||
3038 | for grp_addr in group_addresses: | |
3039 | for destLink, data in topo["routers"][dut]["links"].items(): | |
3040 | if "type" in data and data["type"] == "loopback": | |
3041 | continue | |
3042 | ||
3043 | if "pim" not in topo["routers"][destLink]: | |
3044 | continue | |
3045 | ||
3046 | # Verify RP info | |
3047 | if rp is None: | |
3048 | rp_details = find_rp_details(tgen, topo) | |
3049 | else: | |
660c59ed | 3050 | rp_details = {dut: rp} |
e8cd26fd | 3051 | |
3052 | if dut in rp_details: | |
3053 | pim_nh_intf_ip = topo["routers"][dut]["links"]["lo"]["ipv4"].split( | |
3054 | "/" | |
3055 | )[0] | |
3056 | else: | |
3057 | if destLink not in interface: | |
3058 | continue | |
3059 | ||
3060 | links = topo["routers"][destLink]["links"] | |
3061 | pim_neighbor = {key: links[key] for key in [dut]} | |
3062 | ||
3063 | data = pim_neighbor[dut] | |
3064 | if "pim" in data and data["pim"] == "enable": | |
3065 | pim_nh_intf_ip = data["ipv4"].split("/")[0] | |
3066 | ||
3067 | upstream_rpf_json = show_ip_pim_upstream_rpf_json[grp_addr]["*"] | |
3068 | ||
3069 | # Verifying ip pim upstream rpf | |
3070 | if ( | |
3071 | upstream_rpf_json["rpfInterface"] == interface | |
3072 | and upstream_rpf_json["ribNexthop"] != pim_nh_intf_ip | |
3073 | ): | |
3074 | errormsg = ( | |
3075 | "[DUT %s]: Verifying group: %s, " | |
3076 | "rpf interface: %s, " | |
3077 | " rib Nexthop check [FAILED]!!" | |
3078 | "Expected: %s, Found: %s" | |
3079 | % ( | |
3080 | dut, | |
3081 | grp_addr, | |
3082 | interface, | |
3083 | pim_nh_intf_ip, | |
3084 | upstream_rpf_json["ribNexthop"], | |
3085 | ) | |
3086 | ) | |
3087 | return errormsg | |
3088 | ||
3089 | logger.info( | |
3090 | "[DUT %s]: Verifying group: %s," | |
3091 | " rpf interface: %s, " | |
3092 | " rib Nexthop: %s [PASSED]!!", | |
3093 | dut, | |
3094 | grp_addr, | |
3095 | interface, | |
3096 | pim_nh_intf_ip, | |
3097 | ) | |
3098 | ||
3099 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3100 | return True | |
3101 | ||
3102 | ||
3103 | def enable_disable_pim_unicast_bsm(tgen, router, intf, enable=True): | |
3104 | """ | |
3105 | Helper API to enable or disable pim bsm on interfaces | |
3106 | ||
3107 | Parameters | |
3108 | ---------- | |
3109 | * `tgen` : Topogen object | |
3110 | * `router` : router id to be configured. | |
3111 | * `intf` : Interface to be configured | |
3112 | * `enable` : this flag denotes if config should be enabled or disabled | |
3113 | ||
3114 | Returns | |
3115 | ------- | |
3116 | True or False | |
3117 | """ | |
3118 | result = False | |
3119 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3120 | ||
3121 | try: | |
3122 | config_data = [] | |
3123 | cmd = "interface {}".format(intf) | |
3124 | config_data.append(cmd) | |
3125 | ||
3126 | if enable == True: | |
3127 | config_data.append("ip pim unicast-bsm") | |
3128 | else: | |
3129 | config_data.append("no ip pim unicast-bsm") | |
3130 | ||
3131 | result = create_common_configuration( | |
3132 | tgen, router, config_data, "interface_config", build=False | |
3133 | ) | |
3134 | if result is not True: | |
3135 | return False | |
3136 | ||
3137 | except InvalidCLIError: | |
3138 | # Traceback | |
3139 | errormsg = traceback.format_exc() | |
3140 | logger.error(errormsg) | |
3141 | return errormsg | |
3142 | ||
3143 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3144 | return result | |
3145 | ||
3146 | ||
3147 | def enable_disable_pim_bsm(tgen, router, intf, enable=True): | |
3148 | """ | |
3149 | Helper API to enable or disable pim bsm on interfaces | |
3150 | ||
3151 | Parameters | |
3152 | ---------- | |
3153 | * `tgen` : Topogen object | |
3154 | * `router` : router id to be configured. | |
3155 | * `intf` : Interface to be configured | |
3156 | * `enable` : this flag denotes if config should be enabled or disabled | |
3157 | ||
3158 | Returns | |
3159 | ------- | |
3160 | True or False | |
3161 | """ | |
3162 | result = False | |
3163 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3164 | ||
3165 | try: | |
3166 | config_data = [] | |
3167 | cmd = "interface {}".format(intf) | |
3168 | config_data.append(cmd) | |
3169 | ||
3170 | if enable is True: | |
3171 | config_data.append("ip pim bsm") | |
3172 | else: | |
3173 | config_data.append("no ip pim bsm") | |
3174 | ||
3175 | result = create_common_configuration( | |
3176 | tgen, router, config_data, "interface_config", build=False | |
3177 | ) | |
3178 | if result is not True: | |
3179 | return False | |
3180 | ||
3181 | except InvalidCLIError: | |
3182 | # Traceback | |
3183 | errormsg = traceback.format_exc() | |
3184 | logger.error(errormsg) | |
3185 | return errormsg | |
3186 | ||
3187 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3188 | return result | |
3189 | ||
3190 | ||
de3d6b4d | 3191 | @retry(retry_timeout=60, diag_pct=0) |
4fafd29f | 3192 | def verify_pim_join( |
d7032129 KK |
3193 | tgen, |
3194 | topo, | |
3195 | dut, | |
3196 | interface, | |
3197 | group_addresses, | |
3198 | src_address=None, | |
3199 | addr_type="ipv4", | |
3200 | expected=True, | |
a53c08bc | 3201 | ): |
e8cd26fd | 3202 | """ |
4fafd29f | 3203 | Verify ip/ipv6 pim join by running "show ip/ipv6 pim join" cli |
e8cd26fd | 3204 | |
3205 | Parameters | |
3206 | ---------- | |
3207 | * `tgen`: topogen object | |
3208 | * `topo`: JSON file handler | |
3209 | * `dut`: device under test | |
3210 | * `interface`: interface name, from which PIM join would come | |
3211 | * `group_addresses`: IGMP group address | |
3212 | * `src_address`: Source address | |
3c41ebf8 | 3213 | * `expected` : expected results from API, by-default True |
e8cd26fd | 3214 | |
3215 | Usage | |
3216 | ----- | |
3217 | dut = "r1" | |
3218 | interface = "r1-r0-eth0" | |
3219 | group_address = "225.1.1.1" | |
4fafd29f | 3220 | result = verify_pim_join(tgen, dut, star, group_address, interface) |
e8cd26fd | 3221 | |
3222 | Returns | |
3223 | ------- | |
3224 | errormsg(str) or True | |
3225 | """ | |
3226 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3227 | ||
3228 | if dut not in tgen.routers(): | |
3229 | return False | |
3230 | ||
3231 | rnode = tgen.routers()[dut] | |
3232 | ||
3233 | logger.info("[DUT: %s]: Verifying pim join", dut) | |
e8cd26fd | 3234 | |
3235 | if type(group_addresses) is not list: | |
3236 | group_addresses = [group_addresses] | |
3237 | ||
d7032129 KK |
3238 | for grp in group_addresses: |
3239 | addr_type = validate_ip_address(grp) | |
3240 | ||
3241 | if addr_type == "ipv4": | |
3242 | ip_cmd = "ip" | |
3243 | elif addr_type == "ipv6": | |
3244 | ip_cmd = "ipv6" | |
3245 | ||
3246 | show_pim_join_json = run_frr_cmd( | |
3247 | rnode, "show {} pim join json".format(ip_cmd), isjson=True | |
3248 | ) | |
3249 | ||
e8cd26fd | 3250 | for grp_addr in group_addresses: |
3251 | # Verify if IGMP is enabled in DUT | |
3252 | if "igmp" not in topo["routers"][dut]: | |
3253 | pim_join = True | |
3254 | else: | |
3255 | pim_join = False | |
3256 | ||
3257 | interface_json = show_pim_join_json[interface] | |
3258 | ||
3259 | grp_addr = grp_addr.split("/")[0] | |
3260 | for source, data in interface_json[grp_addr].items(): | |
3261 | ||
3262 | # Verify pim join | |
3263 | if pim_join: | |
3264 | if data["group"] == grp_addr and data["channelJoinName"] == "JOIN": | |
3265 | logger.info( | |
3266 | "[DUT %s]: Verifying pim join for group: %s" | |
3267 | "[PASSED]!! Found Expected: (%s)", | |
3268 | dut, | |
3269 | grp_addr, | |
3270 | data["channelJoinName"], | |
3271 | ) | |
3272 | else: | |
3273 | errormsg = ( | |
3274 | "[DUT %s]: Verifying pim join for group: %s" | |
3275 | "[FAILED]!! Expected: (%s) " | |
3276 | "Found: (%s)" % (dut, grp_addr, "JOIN", data["channelJoinName"]) | |
3277 | ) | |
3278 | return errormsg | |
3279 | ||
3280 | if not pim_join: | |
3281 | if data["group"] == grp_addr and data["channelJoinName"] == "NOINFO": | |
3282 | logger.info( | |
3283 | "[DUT %s]: Verifying pim join for group: %s" | |
3284 | "[PASSED]!! Found Expected: (%s)", | |
3285 | dut, | |
3286 | grp_addr, | |
3287 | data["channelJoinName"], | |
3288 | ) | |
3289 | else: | |
3290 | errormsg = ( | |
3291 | "[DUT %s]: Verifying pim join for group: %s" | |
3292 | "[FAILED]!! Expected: (%s) " | |
3293 | "Found: (%s)" | |
3294 | % (dut, grp_addr, "NOINFO", data["channelJoinName"]) | |
3295 | ) | |
3296 | return errormsg | |
3297 | ||
3298 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3299 | return True | |
3300 | ||
3301 | ||
de3d6b4d | 3302 | @retry(retry_timeout=60, diag_pct=0) |
3c41ebf8 | 3303 | def verify_igmp_config(tgen, input_dict, stats_return=False, expected=True): |
e8cd26fd | 3304 | """ |
3305 | Verify igmp interface details, verifying following configs: | |
3306 | timerQueryInterval | |
3307 | timerQueryResponseIntervalMsec | |
3308 | lastMemberQueryCount | |
3309 | timerLastMemberQueryMsec | |
3310 | ||
3311 | Parameters | |
3312 | ---------- | |
3313 | * `tgen`: topogen object | |
3314 | * `input_dict` : Input dict data, required to verify | |
3315 | timer | |
3316 | * `stats_return`: If user wants API to return statistics | |
3c41ebf8 | 3317 | * `expected` : expected results from API, by-default True |
e8cd26fd | 3318 | |
3319 | Usage | |
3320 | ----- | |
3321 | input_dict ={ | |
3322 | "l1": { | |
3323 | "igmp": { | |
3324 | "interfaces": { | |
3325 | "l1-i1-eth1": { | |
3326 | "igmp": { | |
3327 | "query": { | |
3328 | "query-interval" : 200, | |
3329 | "query-max-response-time" : 100 | |
3330 | }, | |
3331 | "statistics": { | |
3332 | "queryV2" : 2, | |
3333 | "reportV2" : 1 | |
3334 | } | |
3335 | } | |
3336 | } | |
3337 | } | |
3338 | } | |
3339 | } | |
3340 | } | |
3341 | result = verify_igmp_config(tgen, input_dict, stats_return) | |
3342 | ||
3343 | Returns | |
3344 | ------- | |
3345 | errormsg(str) or True | |
3346 | """ | |
3347 | ||
3348 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3349 | ||
3350 | for dut in input_dict.keys(): | |
3351 | rnode = tgen.routers()[dut] | |
3352 | ||
3353 | for interface, data in input_dict[dut]["igmp"]["interfaces"].items(): | |
3354 | ||
3355 | statistics = False | |
3356 | report = False | |
3357 | if "statistics" in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"]: | |
3358 | statistics = True | |
3359 | cmd = "show ip igmp statistics" | |
3360 | else: | |
3361 | cmd = "show ip igmp" | |
3362 | ||
3363 | logger.info( | |
3364 | "[DUT: %s]: Verifying IGMP interface %s detail:", dut, interface | |
3365 | ) | |
3366 | ||
3367 | if statistics: | |
3368 | if ( | |
3369 | "report" | |
3370 | in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"][ | |
3371 | "statistics" | |
3372 | ] | |
3373 | ): | |
3374 | report = True | |
3375 | ||
3376 | if statistics and report: | |
3377 | show_ip_igmp_intf_json = run_frr_cmd( | |
5aab262b | 3378 | rnode, "{} json".format(cmd), isjson=True |
e8cd26fd | 3379 | ) |
3380 | intf_detail_json = show_ip_igmp_intf_json["global"] | |
3381 | else: | |
3382 | show_ip_igmp_intf_json = run_frr_cmd( | |
3383 | rnode, "{} interface {} json".format(cmd, interface), isjson=True | |
3384 | ) | |
3385 | ||
3386 | if not report: | |
3387 | if interface not in show_ip_igmp_intf_json: | |
3388 | errormsg = ( | |
3389 | "[DUT %s]: IGMP interface: %s " | |
3390 | " is not present in CLI output " | |
3391 | "[FAILED]!! " % (dut, interface) | |
3392 | ) | |
3393 | return errormsg | |
3394 | ||
3395 | else: | |
3396 | intf_detail_json = show_ip_igmp_intf_json[interface] | |
3397 | ||
3398 | if stats_return: | |
3399 | igmp_stats = {} | |
3400 | ||
3401 | if "statistics" in data["igmp"]: | |
3402 | if stats_return: | |
3403 | igmp_stats["statistics"] = {} | |
3404 | for query, value in data["igmp"]["statistics"].items(): | |
3405 | if query == "queryV2": | |
3406 | # Verifying IGMP interface queryV2 statistics | |
3407 | if stats_return: | |
3408 | igmp_stats["statistics"][query] = intf_detail_json[ | |
3409 | "queryV2" | |
3410 | ] | |
3411 | ||
3412 | else: | |
3413 | if intf_detail_json["queryV2"] != value: | |
3414 | errormsg = ( | |
3415 | "[DUT %s]: IGMP interface: %s " | |
3416 | " queryV2 statistics verification " | |
3417 | "[FAILED]!! Expected : %s," | |
3418 | " Found : %s" | |
3419 | % ( | |
3420 | dut, | |
3421 | interface, | |
3422 | value, | |
3423 | intf_detail_json["queryV2"], | |
3424 | ) | |
3425 | ) | |
3426 | return errormsg | |
3427 | ||
3428 | logger.info( | |
3429 | "[DUT %s]: IGMP interface: %s " | |
3430 | "queryV2 statistics is %s", | |
3431 | dut, | |
3432 | interface, | |
3433 | value, | |
3434 | ) | |
3435 | ||
3436 | if query == "reportV2": | |
3437 | # Verifying IGMP interface timerV2 statistics | |
3438 | if stats_return: | |
3439 | igmp_stats["statistics"][query] = intf_detail_json[ | |
3440 | "reportV2" | |
3441 | ] | |
3442 | ||
3443 | else: | |
3444 | if intf_detail_json["reportV2"] <= value: | |
3445 | errormsg = ( | |
3446 | "[DUT %s]: IGMP reportV2 " | |
3447 | "statistics verification " | |
3448 | "[FAILED]!! Expected : %s " | |
3449 | "or more, Found : %s" | |
3450 | % ( | |
3451 | dut, | |
3452 | interface, | |
3453 | value, | |
e8cd26fd | 3454 | ) |
3455 | ) | |
3456 | return errormsg | |
3457 | ||
3458 | logger.info( | |
3459 | "[DUT %s]: IGMP reportV2 " "statistics is %s", | |
3460 | dut, | |
3461 | intf_detail_json["reportV2"], | |
3462 | ) | |
3463 | ||
3464 | if "query" in data["igmp"]: | |
3465 | for query, value in data["igmp"]["query"].items(): | |
3466 | if query == "query-interval": | |
3467 | # Verifying IGMP interface query interval timer | |
3468 | if intf_detail_json["timerQueryInterval"] != value: | |
3469 | errormsg = ( | |
3470 | "[DUT %s]: IGMP interface: %s " | |
3471 | " query-interval verification " | |
3472 | "[FAILED]!! Expected : %s," | |
3473 | " Found : %s" | |
3474 | % ( | |
3475 | dut, | |
3476 | interface, | |
3477 | value, | |
3478 | intf_detail_json["timerQueryInterval"], | |
3479 | ) | |
3480 | ) | |
3481 | return errormsg | |
3482 | ||
3483 | logger.info( | |
3484 | "[DUT %s]: IGMP interface: %s " "query-interval is %s", | |
3485 | dut, | |
3486 | interface, | |
3487 | value, | |
3488 | ) | |
3489 | ||
3490 | if query == "query-max-response-time": | |
3491 | # Verifying IGMP interface query max response timer | |
3492 | if ( | |
3493 | intf_detail_json["timerQueryResponseIntervalMsec"] | |
3494 | != value * 100 | |
3495 | ): | |
3496 | errormsg = ( | |
3497 | "[DUT %s]: IGMP interface: %s " | |
3498 | "query-max-response-time " | |
3499 | "verification [FAILED]!!" | |
3500 | " Expected : %s, Found : %s" | |
3501 | % ( | |
3502 | dut, | |
3503 | interface, | |
3504 | value * 1000, | |
3505 | intf_detail_json["timerQueryResponseIntervalMsec"], | |
3506 | ) | |
3507 | ) | |
3508 | return errormsg | |
3509 | ||
3510 | logger.info( | |
3511 | "[DUT %s]: IGMP interface: %s " | |
3512 | "query-max-response-time is %s ms", | |
3513 | dut, | |
3514 | interface, | |
3515 | value * 100, | |
3516 | ) | |
3517 | ||
3518 | if query == "last-member-query-count": | |
3519 | # Verifying IGMP interface last member query count | |
3520 | if intf_detail_json["lastMemberQueryCount"] != value: | |
3521 | errormsg = ( | |
3522 | "[DUT %s]: IGMP interface: %s " | |
3523 | "last-member-query-count " | |
3524 | "verification [FAILED]!!" | |
3525 | " Expected : %s, Found : %s" | |
3526 | % ( | |
3527 | dut, | |
3528 | interface, | |
3529 | value, | |
3530 | intf_detail_json["lastMemberQueryCount"], | |
3531 | ) | |
3532 | ) | |
3533 | return errormsg | |
3534 | ||
3535 | logger.info( | |
3536 | "[DUT %s]: IGMP interface: %s " | |
3537 | "last-member-query-count is %s ms", | |
3538 | dut, | |
3539 | interface, | |
3540 | value * 1000, | |
3541 | ) | |
3542 | ||
3543 | if query == "last-member-query-interval": | |
3544 | # Verifying IGMP interface last member query interval | |
3545 | if ( | |
3546 | intf_detail_json["timerLastMemberQueryMsec"] | |
3547 | != value * 100 * intf_detail_json["lastMemberQueryCount"] | |
3548 | ): | |
3549 | errormsg = ( | |
3550 | "[DUT %s]: IGMP interface: %s " | |
3551 | "last-member-query-interval " | |
3552 | "verification [FAILED]!!" | |
3553 | " Expected : %s, Found : %s" | |
3554 | % ( | |
3555 | dut, | |
3556 | interface, | |
3557 | value * 1000, | |
3558 | intf_detail_json["timerLastMemberQueryMsec"], | |
3559 | ) | |
3560 | ) | |
3561 | return errormsg | |
3562 | ||
3563 | logger.info( | |
3564 | "[DUT %s]: IGMP interface: %s " | |
3565 | "last-member-query-interval is %s ms", | |
3566 | dut, | |
3567 | interface, | |
3568 | value * intf_detail_json["lastMemberQueryCount"] * 100, | |
3569 | ) | |
3570 | ||
3571 | if "version" in data["igmp"]: | |
3572 | # Verifying IGMP interface state is up | |
3573 | if intf_detail_json["state"] != "up": | |
3574 | errormsg = ( | |
3575 | "[DUT %s]: IGMP interface: %s " | |
3576 | " state: %s verification " | |
3577 | "[FAILED]!!" % (dut, interface, intf_detail_json["state"]) | |
3578 | ) | |
3579 | return errormsg | |
3580 | ||
3581 | logger.info( | |
3582 | "[DUT %s]: IGMP interface: %s " "state: %s", | |
3583 | dut, | |
3584 | interface, | |
3585 | intf_detail_json["state"], | |
3586 | ) | |
3587 | ||
3588 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3589 | return True if stats_return == False else igmp_stats | |
3590 | ||
3591 | ||
de3d6b4d | 3592 | @retry(retry_timeout=60, diag_pct=0) |
3c41ebf8 | 3593 | def verify_pim_config(tgen, input_dict, expected=True): |
e8cd26fd | 3594 | """ |
3595 | Verify pim interface details, verifying following configs: | |
3596 | drPriority | |
3597 | helloPeriod | |
3598 | helloReceived | |
3599 | helloSend | |
3600 | drAddress | |
3601 | ||
3602 | Parameters | |
3603 | ---------- | |
3604 | * `tgen`: topogen object | |
3605 | * `input_dict` : Input dict data, required to verify | |
3606 | timer | |
3c41ebf8 | 3607 | * `expected` : expected results from API, by-default True |
e8cd26fd | 3608 | |
3609 | Usage | |
3610 | ----- | |
3611 | input_dict ={ | |
3612 | "l1": { | |
3613 | "igmp": { | |
3614 | "interfaces": { | |
3615 | "l1-i1-eth1": { | |
3616 | "pim": { | |
3617 | "drPriority" : 10, | |
3618 | "helloPeriod" : 5 | |
3619 | } | |
3620 | } | |
3621 | } | |
3622 | } | |
3623 | } | |
3624 | } | |
3625 | } | |
3626 | result = verify_pim_config(tgen, input_dict) | |
3627 | ||
3628 | Returns | |
3629 | ------- | |
3630 | errormsg(str) or True | |
3631 | """ | |
3632 | ||
3633 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3634 | ||
3635 | for dut in input_dict.keys(): | |
3636 | rnode = tgen.routers()[dut] | |
3637 | ||
3638 | for interface, data in input_dict[dut]["pim"]["interfaces"].items(): | |
3639 | ||
3640 | logger.info("[DUT: %s]: Verifying PIM interface %s detail:", dut, interface) | |
3641 | ||
3642 | show_ip_igmp_intf_json = run_frr_cmd( | |
3643 | rnode, "show ip pim interface {} json".format(interface), isjson=True | |
3644 | ) | |
3645 | ||
3646 | if interface not in show_ip_igmp_intf_json: | |
3647 | errormsg = ( | |
3648 | "[DUT %s]: PIM interface: %s " | |
3649 | " is not present in CLI output " | |
3650 | "[FAILED]!! " % (dut, interface) | |
3651 | ) | |
3652 | return errormsg | |
3653 | ||
3654 | intf_detail_json = show_ip_igmp_intf_json[interface] | |
3655 | ||
3656 | for config, value in data.items(): | |
3657 | if config == "helloPeriod": | |
3658 | # Verifying PIM interface helloPeriod | |
3659 | if intf_detail_json["helloPeriod"] != value: | |
3660 | errormsg = ( | |
3661 | "[DUT %s]: PIM interface: %s " | |
3662 | " helloPeriod verification " | |
3663 | "[FAILED]!! Expected : %s," | |
3664 | " Found : %s" | |
3665 | % (dut, interface, value, intf_detail_json["helloPeriod"]) | |
3666 | ) | |
3667 | return errormsg | |
3668 | ||
3669 | logger.info( | |
3670 | "[DUT %s]: PIM interface: %s " "helloPeriod is %s", | |
3671 | dut, | |
3672 | interface, | |
3673 | value, | |
3674 | ) | |
3675 | ||
3676 | if config == "drPriority": | |
3677 | # Verifying PIM interface drPriority | |
3678 | if intf_detail_json["drPriority"] != value: | |
3679 | errormsg = ( | |
3680 | "[DUT %s]: PIM interface: %s " | |
3681 | " drPriority verification " | |
3682 | "[FAILED]!! Expected : %s," | |
3683 | " Found : %s" | |
3684 | % (dut, interface, value, intf_detail_json["drPriority"]) | |
3685 | ) | |
3686 | return errormsg | |
3687 | ||
3688 | logger.info( | |
3689 | "[DUT %s]: PIM interface: %s " "drPriority is %s", | |
3690 | dut, | |
3691 | interface, | |
3692 | value, | |
3693 | ) | |
3694 | ||
3695 | if config == "drAddress": | |
3696 | # Verifying PIM interface drAddress | |
3697 | if intf_detail_json["drAddress"] != value: | |
3698 | errormsg = ( | |
3699 | "[DUT %s]: PIM interface: %s " | |
3700 | " drAddress verification " | |
3701 | "[FAILED]!! Expected : %s," | |
3702 | " Found : %s" | |
3703 | % (dut, interface, value, intf_detail_json["drAddress"]) | |
3704 | ) | |
3705 | return errormsg | |
3706 | ||
3707 | logger.info( | |
3708 | "[DUT %s]: PIM interface: %s " "drAddress is %s", | |
3709 | dut, | |
3710 | interface, | |
3711 | value, | |
3712 | ) | |
3713 | ||
3714 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3715 | return True | |
3716 | ||
3717 | ||
de3d6b4d | 3718 | @retry(retry_timeout=20, diag_pct=0) |
3c41ebf8 | 3719 | def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=True): |
e8cd26fd | 3720 | """ |
3721 | Verify multicast traffic by running | |
3722 | "show multicast traffic count json" cli | |
3723 | ||
3724 | Parameters | |
3725 | ---------- | |
3726 | * `tgen`: topogen object | |
3727 | * `input_dict(dict)`: defines DUT, what and for which interfaces | |
3728 | traffic needs to be verified | |
3729 | * `return_traffic`: returns traffic stats | |
3c41ebf8 KK |
3730 | * `expected` : expected results from API, by-default True |
3731 | ||
e8cd26fd | 3732 | Usage |
3733 | ----- | |
3734 | input_dict = { | |
3735 | "r1": { | |
3736 | "traffic_received": ["r1-r0-eth0"], | |
3737 | "traffic_sent": ["r1-r0-eth0"] | |
3738 | } | |
3739 | } | |
3740 | ||
3741 | result = verify_multicast_traffic(tgen, input_dict) | |
3742 | ||
3743 | Returns | |
3744 | ------- | |
3745 | errormsg(str) or True | |
3746 | """ | |
3747 | ||
3748 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3749 | ||
3750 | traffic_dict = {} | |
3751 | for dut in input_dict.keys(): | |
3752 | if dut not in tgen.routers(): | |
3753 | continue | |
3754 | ||
3755 | rnode = tgen.routers()[dut] | |
3756 | ||
3757 | logger.info("[DUT: %s]: Verifying multicast " "traffic", dut) | |
3758 | ||
3759 | show_multicast_traffic_json = run_frr_cmd( | |
3760 | rnode, "show ip multicast count json", isjson=True | |
3761 | ) | |
3762 | ||
3763 | for traffic_type, interfaces in input_dict[dut].items(): | |
3764 | traffic_dict[traffic_type] = {} | |
3765 | if traffic_type == "traffic_received": | |
3766 | for interface in interfaces: | |
3767 | traffic_dict[traffic_type][interface] = {} | |
3768 | interface_json = show_multicast_traffic_json[interface] | |
3769 | ||
3770 | if interface_json["pktsIn"] == 0 and interface_json["bytesIn"] == 0: | |
3771 | errormsg = ( | |
3772 | "[DUT %s]: Multicast traffic is " | |
3773 | "not received on interface %s " | |
3774 | "PktsIn: %s, BytesIn: %s " | |
3775 | "[FAILED]!!" | |
3776 | % ( | |
3777 | dut, | |
3778 | interface, | |
3779 | interface_json["pktsIn"], | |
3780 | interface_json["bytesIn"], | |
3781 | ) | |
3782 | ) | |
3783 | return errormsg | |
3784 | ||
3785 | elif ( | |
3786 | interface_json["pktsIn"] != 0 and interface_json["bytesIn"] != 0 | |
3787 | ): | |
3788 | ||
3789 | traffic_dict[traffic_type][interface][ | |
3790 | "pktsIn" | |
3791 | ] = interface_json["pktsIn"] | |
3792 | traffic_dict[traffic_type][interface][ | |
3793 | "bytesIn" | |
3794 | ] = interface_json["bytesIn"] | |
3795 | ||
3796 | logger.info( | |
3797 | "[DUT %s]: Multicast traffic is " | |
3798 | "received on interface %s " | |
3799 | "PktsIn: %s, BytesIn: %s " | |
3800 | "[PASSED]!!" | |
3801 | % ( | |
3802 | dut, | |
3803 | interface, | |
3804 | interface_json["pktsIn"], | |
3805 | interface_json["bytesIn"], | |
3806 | ) | |
3807 | ) | |
3808 | ||
3809 | else: | |
3810 | errormsg = ( | |
3811 | "[DUT %s]: Multicast traffic interface %s:" | |
3812 | " Miss-match in " | |
3813 | "PktsIn: %s, BytesIn: %s" | |
3814 | "[FAILED]!!" | |
3815 | % ( | |
3816 | dut, | |
3817 | interface, | |
3818 | interface_json["pktsIn"], | |
3819 | interface_json["bytesIn"], | |
3820 | ) | |
3821 | ) | |
3822 | return errormsg | |
3823 | ||
3824 | if traffic_type == "traffic_sent": | |
3825 | traffic_dict[traffic_type] = {} | |
3826 | for interface in interfaces: | |
3827 | traffic_dict[traffic_type][interface] = {} | |
3828 | interface_json = show_multicast_traffic_json[interface] | |
3829 | ||
3830 | if ( | |
3831 | interface_json["pktsOut"] == 0 | |
3832 | and interface_json["bytesOut"] == 0 | |
3833 | ): | |
3834 | errormsg = ( | |
3835 | "[DUT %s]: Multicast traffic is " | |
3836 | "not received on interface %s " | |
3837 | "PktsIn: %s, BytesIn: %s" | |
3838 | "[FAILED]!!" | |
3839 | % ( | |
3840 | dut, | |
3841 | interface, | |
3842 | interface_json["pktsOut"], | |
3843 | interface_json["bytesOut"], | |
3844 | ) | |
3845 | ) | |
3846 | return errormsg | |
3847 | ||
3848 | elif ( | |
3849 | interface_json["pktsOut"] != 0 | |
3850 | and interface_json["bytesOut"] != 0 | |
3851 | ): | |
3852 | ||
3853 | traffic_dict[traffic_type][interface][ | |
3854 | "pktsOut" | |
3855 | ] = interface_json["pktsOut"] | |
3856 | traffic_dict[traffic_type][interface][ | |
3857 | "bytesOut" | |
3858 | ] = interface_json["bytesOut"] | |
3859 | ||
3860 | logger.info( | |
3861 | "[DUT %s]: Multicast traffic is " | |
3862 | "received on interface %s " | |
3863 | "PktsOut: %s, BytesOut: %s " | |
3864 | "[PASSED]!!" | |
3865 | % ( | |
3866 | dut, | |
3867 | interface, | |
3868 | interface_json["pktsOut"], | |
3869 | interface_json["bytesOut"], | |
3870 | ) | |
3871 | ) | |
3872 | else: | |
3873 | errormsg = ( | |
3874 | "[DUT %s]: Multicast traffic interface %s:" | |
3875 | " Miss-match in " | |
3876 | "PktsOut: %s, BytesOut: %s " | |
3877 | "[FAILED]!!" | |
3878 | % ( | |
3879 | dut, | |
3880 | interface, | |
3881 | interface_json["pktsOut"], | |
3882 | interface_json["bytesOut"], | |
3883 | ) | |
3884 | ) | |
3885 | return errormsg | |
3886 | ||
3887 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3888 | return True if return_traffic == False else traffic_dict | |
3889 | ||
3890 | ||
3891 | def get_refCount_for_mroute(tgen, dut, iif, src_address, group_addresses): | |
3892 | """ | |
3893 | Verify upstream inbound interface is updated correctly | |
3894 | by running "show ip pim upstream" cli | |
3895 | ||
3896 | Parameters | |
3897 | ---------- | |
3898 | * `tgen`: topogen object | |
3899 | * `dut`: device under test | |
3900 | * `iif`: inbound interface | |
3901 | * `src_address`: source address | |
3902 | * `group_addresses`: IGMP group address | |
3903 | ||
3904 | Usage | |
3905 | ----- | |
3906 | dut = "r1" | |
3907 | iif = "r1-r0-eth0" | |
3908 | src_address = "*" | |
3909 | group_address = "225.1.1.1" | |
3910 | result = get_refCount_for_mroute(tgen, dut, iif, src_address, | |
3911 | group_address) | |
3912 | ||
3913 | Returns | |
3914 | ------- | |
3915 | refCount(int) | |
3916 | """ | |
3917 | ||
3918 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3919 | ||
3920 | refCount = 0 | |
3921 | if dut not in tgen.routers(): | |
3922 | return False | |
3923 | ||
3924 | rnode = tgen.routers()[dut] | |
3925 | ||
3926 | logger.info("[DUT: %s]: Verifying refCount for mroutes: ", dut) | |
3927 | show_ip_pim_upstream_json = run_frr_cmd( | |
3928 | rnode, "show ip pim upstream json", isjson=True | |
3929 | ) | |
3930 | ||
3931 | if type(group_addresses) is not list: | |
3932 | group_addresses = [group_addresses] | |
3933 | ||
3934 | for grp_addr in group_addresses: | |
3935 | # Verify group address | |
3936 | if grp_addr not in show_ip_pim_upstream_json: | |
3937 | errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % ( | |
3938 | dut, | |
3939 | grp_addr, | |
3940 | ) | |
3941 | return errormsg | |
3942 | group_addr_json = show_ip_pim_upstream_json[grp_addr] | |
3943 | ||
3944 | # Verify source address | |
3945 | if src_address not in group_addr_json: | |
3946 | errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % ( | |
3947 | dut, | |
3948 | src_address, | |
3949 | grp_addr, | |
3950 | ) | |
3951 | return errormsg | |
3952 | ||
3953 | # Verify Inbound Interface | |
3954 | if group_addr_json[src_address]["inboundInterface"] == iif: | |
3955 | refCount = group_addr_json[src_address]["refCount"] | |
3956 | ||
3957 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3958 | return refCount | |
3959 | ||
3960 | ||
de3d6b4d | 3961 | @retry(retry_timeout=40, diag_pct=0) |
a53c08bc CH |
3962 | def verify_multicast_flag_state( |
3963 | tgen, dut, src_address, group_addresses, flag, expected=True | |
3964 | ): | |
e8cd26fd | 3965 | """ |
3966 | Verify flag state for mroutes and make sure (*, G)/(S, G) are having | |
3967 | coorect flags by running "show ip mroute" cli | |
3968 | ||
3969 | Parameters | |
3970 | ---------- | |
3971 | * `tgen`: topogen object | |
3972 | * `dut`: device under test | |
3973 | * `src_address`: source address | |
3974 | * `group_addresses`: IGMP group address | |
3975 | * `flag`: flag state, needs to be verified | |
3c41ebf8 | 3976 | * `expected` : expected results from API, by-default True |
e8cd26fd | 3977 | |
3978 | Usage | |
3979 | ----- | |
3980 | dut = "r1" | |
3981 | flag = "SC" | |
3982 | group_address = "225.1.1.1" | |
3983 | result = verify_multicast_flag_state(tgen, dut, src_address, | |
3984 | group_address, flag) | |
3985 | ||
3986 | Returns | |
3987 | ------- | |
3988 | errormsg(str) or True | |
3989 | """ | |
3990 | ||
3991 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3992 | ||
3993 | if dut not in tgen.routers(): | |
3994 | return False | |
3995 | ||
3996 | rnode = tgen.routers()[dut] | |
3997 | ||
3998 | logger.info("[DUT: %s]: Verifying flag state for mroutes", dut) | |
3999 | show_ip_mroute_json = run_frr_cmd(rnode, "show ip mroute json", isjson=True) | |
4000 | ||
4001 | if bool(show_ip_mroute_json) == False: | |
4002 | error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut) | |
4003 | return error_msg | |
4004 | ||
4005 | if type(group_addresses) is not list: | |
4006 | group_addresses = [group_addresses] | |
4007 | ||
4008 | for grp_addr in group_addresses: | |
4009 | if grp_addr not in show_ip_mroute_json: | |
4010 | errormsg = ( | |
4011 | "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! ", | |
4012 | dut, | |
4013 | src_address, | |
4014 | grp_addr, | |
4015 | ) | |
4016 | return errormsg | |
4017 | else: | |
4018 | group_addr_json = show_ip_mroute_json[grp_addr] | |
4019 | ||
4020 | if src_address not in group_addr_json: | |
4021 | errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % ( | |
4022 | dut, | |
4023 | src_address, | |
4024 | grp_addr, | |
4025 | ) | |
4026 | return errormsg | |
4027 | else: | |
4028 | mroutes = group_addr_json[src_address] | |
4029 | ||
4030 | if mroutes["installed"] != 0: | |
4031 | logger.info( | |
4032 | "[DUT %s]: mroute (%s,%s) is installed", dut, src_address, grp_addr | |
4033 | ) | |
4034 | ||
4035 | if mroutes["flags"] != flag: | |
4036 | errormsg = ( | |
4037 | "[DUT %s]: Verifying flag for (%s, %s) " | |
4038 | "mroute [FAILED]!! " | |
4039 | "Expected: %s Found: %s" | |
4040 | % (dut, src_address, grp_addr, flag, mroutes["flags"]) | |
4041 | ) | |
4042 | return errormsg | |
4043 | ||
4044 | logger.info( | |
4045 | "[DUT %s]: Verifying flag for (%s, %s)" | |
4046 | " mroute, [PASSED]!! " | |
4047 | "Found Expected: %s", | |
4048 | dut, | |
4049 | src_address, | |
4050 | grp_addr, | |
4051 | mroutes["flags"], | |
4052 | ) | |
4053 | ||
4054 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
4055 | return True | |
eab72dc8 | 4056 | |
4057 | ||
de3d6b4d | 4058 | @retry(retry_timeout=40, diag_pct=0) |
d7032129 | 4059 | def verify_igmp_interface(tgen, dut, igmp_iface, interface_ip, expected=True): |
eab72dc8 | 4060 | """ |
4061 | Verify all IGMP interface are up and running, config is verified | |
4062 | using "show ip igmp interface" cli | |
4063 | ||
4064 | Parameters | |
4065 | ---------- | |
4066 | * `tgen`: topogen object | |
4067 | * `topo` : json file data | |
4068 | * `dut` : device under test | |
4069 | * `igmp_iface` : interface name | |
4070 | * `interface_ip` : interface ip address | |
3c41ebf8 | 4071 | * `expected` : expected results from API, by-default True |
eab72dc8 | 4072 | |
4073 | Usage | |
4074 | ----- | |
4075 | result = verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip) | |
4076 | ||
4077 | Returns | |
4078 | ------- | |
4079 | errormsg(str) or True | |
4080 | """ | |
4081 | ||
4082 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
4083 | ||
4084 | for router in tgen.routers(): | |
4085 | if router != dut: | |
4086 | continue | |
4087 | ||
5980ad0a | 4088 | logger.info("[DUT: %s]: Verifying PIM interface status:", dut) |
eab72dc8 | 4089 | |
4090 | rnode = tgen.routers()[dut] | |
5980ad0a DS |
4091 | show_ip_igmp_interface_json = run_frr_cmd( |
4092 | rnode, "show ip igmp interface json", isjson=True | |
4093 | ) | |
eab72dc8 | 4094 | |
5980ad0a | 4095 | if igmp_iface in show_ip_igmp_interface_json: |
eab72dc8 | 4096 | igmp_intf_json = show_ip_igmp_interface_json[igmp_iface] |
4097 | # Verifying igmp interface | |
5980ad0a DS |
4098 | if igmp_intf_json["address"] != interface_ip: |
4099 | errormsg = ( | |
4100 | "[DUT %s]: igmp interface ip is not correct " | |
4101 | "[FAILED]!! Expected : %s, Found : %s" | |
4102 | % (dut, igmp_intf_json["address"], interface_ip) | |
4103 | ) | |
eab72dc8 | 4104 | return errormsg |
4105 | ||
5980ad0a DS |
4106 | logger.info( |
4107 | "[DUT %s]: igmp interface: %s, " "interface ip: %s" " [PASSED]!!", | |
4108 | dut, | |
4109 | igmp_iface, | |
4110 | interface_ip, | |
4111 | ) | |
eab72dc8 | 4112 | else: |
5980ad0a DS |
4113 | errormsg = ( |
4114 | "[DUT %s]: igmp interface: %s " | |
4115 | "igmp interface ip: %s, is not present " | |
4116 | % (dut, igmp_iface, interface_ip) | |
4117 | ) | |
eab72dc8 | 4118 | return errormsg |
4119 | ||
4120 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
4121 | return True | |
a5124c49 CH |
4122 | |
4123 | ||
a53c08bc | 4124 | class McastTesterHelper(HostApplicationHelper): |
a5124c49 CH |
4125 | def __init__(self, tgen=None): |
4126 | self.script_path = os.path.join(CWD, "mcast-tester.py") | |
4127 | self.host_conn = {} | |
4128 | self.listen_sock = None | |
4129 | ||
4130 | # # Get a temporary file for socket path | |
4131 | # (fd, sock_path) = tempfile.mkstemp("-mct.sock", "tmp" + str(os.getpid())) | |
4132 | # os.close(fd) | |
4133 | # os.remove(sock_path) | |
4134 | # self.app_sock_path = sock_path | |
4135 | ||
4136 | # # Listen on unix socket | |
4137 | # logger.debug("%s: listening on socket %s", self, self.app_sock_path) | |
4138 | # self.listen_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) | |
4139 | # self.listen_sock.settimeout(10) | |
4140 | # self.listen_sock.bind(self.app_sock_path) | |
4141 | # self.listen_sock.listen(10) | |
4142 | ||
4143 | python3_path = get_exec_path(["python3", "python"]) | |
4144 | super(McastTesterHelper, self).__init__( | |
4145 | tgen, | |
4146 | # [python3_path, self.script_path, self.app_sock_path] | |
a53c08bc | 4147 | [python3_path, self.script_path], |
a5124c49 CH |
4148 | ) |
4149 | ||
a53c08bc | 4150 | def __str__(self): |
a5124c49 CH |
4151 | return "McastTesterHelper({})".format(self.script_path) |
4152 | ||
4153 | def run_join(self, host, join_addrs, join_towards=None, join_intf=None): | |
4154 | """ | |
4155 | Join a UDP multicast group. | |
4156 | ||
4157 | One of join_towards or join_intf MUST be set. | |
4158 | ||
4159 | Parameters: | |
4160 | ----------- | |
4161 | * `host`: host from where IGMP join would be sent | |
4162 | * `join_addrs`: multicast address (or addresses) to join to | |
4163 | * `join_intf`: the interface to bind the join[s] to | |
4164 | * `join_towards`: router whos interface to bind the join[s] to | |
4165 | """ | |
4166 | if not isinstance(join_addrs, list) and not isinstance(join_addrs, tuple): | |
4167 | join_addrs = [join_addrs] | |
4168 | ||
4169 | if join_towards: | |
a53c08bc CH |
4170 | join_intf = frr_unicode( |
4171 | self.tgen.json_topo["routers"][host]["links"][join_towards]["interface"] | |
4172 | ) | |
a5124c49 CH |
4173 | else: |
4174 | assert join_intf | |
4175 | ||
4176 | for join in join_addrs: | |
4177 | self.run(host, [join, join_intf]) | |
4178 | ||
4179 | return True | |
4180 | ||
4181 | def run_traffic(self, host, send_to_addrs, bind_towards=None, bind_intf=None): | |
4182 | """ | |
4183 | Send UDP multicast traffic. | |
4184 | ||
4185 | One of bind_towards or bind_intf MUST be set. | |
4186 | ||
4187 | Parameters: | |
4188 | ----------- | |
4189 | * `host`: host to send traffic from | |
4190 | * `send_to_addrs`: multicast address (or addresses) to send traffic to | |
4191 | * `bind_towards`: Router who's interface the source ip address is got from | |
4192 | """ | |
4193 | if bind_towards: | |
a53c08bc CH |
4194 | bind_intf = frr_unicode( |
4195 | self.tgen.json_topo["routers"][host]["links"][bind_towards]["interface"] | |
4196 | ) | |
a5124c49 CH |
4197 | else: |
4198 | assert bind_intf | |
4199 | ||
4200 | if not isinstance(send_to_addrs, list) and not isinstance(send_to_addrs, tuple): | |
4201 | send_to_addrs = [send_to_addrs] | |
4202 | ||
4203 | for send_to in send_to_addrs: | |
4204 | self.run(host, ["--send=0.7", send_to, bind_intf]) | |
4205 | ||
4206 | return True | |
4207 | ||
c28e6ef5 | 4208 | |
e46ce55e KK |
4209 | @retry(retry_timeout=62) |
4210 | def verify_local_igmp_groups(tgen, dut, interface, group_addresses): | |
4211 | """ | |
4212 | Verify local IGMP groups are received from an intended interface | |
4213 | by running "show ip igmp join json" command | |
4214 | ||
4215 | Parameters | |
4216 | ---------- | |
4217 | * `tgen`: topogen object | |
4218 | * `dut`: device under test | |
4219 | * `interface`: interface, from which IGMP groups are configured | |
4220 | * `group_addresses`: IGMP group address | |
4221 | ||
4222 | Usage | |
4223 | ----- | |
4224 | dut = "r1" | |
4225 | interface = "r1-r0-eth0" | |
4226 | group_address = "225.1.1.1" | |
4227 | result = verify_local_igmp_groups(tgen, dut, interface, group_address) | |
4228 | ||
4229 | Returns | |
4230 | ------- | |
4231 | errormsg(str) or True | |
4232 | """ | |
4233 | ||
4234 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
4235 | ||
4236 | if dut not in tgen.routers(): | |
4237 | return False | |
4238 | ||
4239 | rnode = tgen.routers()[dut] | |
4240 | ||
4241 | logger.info("[DUT: %s]: Verifying local IGMP groups received:", dut) | |
4242 | show_ip_local_igmp_json = run_frr_cmd(rnode, "show ip igmp join json", isjson=True) | |
4243 | ||
4244 | if type(group_addresses) is not list: | |
4245 | group_addresses = [group_addresses] | |
4246 | ||
4247 | if interface not in show_ip_local_igmp_json: | |
4248 | ||
4249 | errormsg = ( | |
4250 | "[DUT %s]: Verifying local IGMP group received" | |
4251 | " from interface %s [FAILED]!! " % (dut, interface) | |
4252 | ) | |
4253 | return errormsg | |
4254 | ||
4255 | for grp_addr in group_addresses: | |
4256 | found = False | |
4257 | for index in show_ip_local_igmp_json[interface]["groups"]: | |
4258 | if index["group"] == grp_addr: | |
4259 | found = True | |
4260 | break | |
4261 | if not found: | |
4262 | errormsg = ( | |
4263 | "[DUT %s]: Verifying local IGMP group received" | |
4264 | " from interface %s [FAILED]!! " | |
4265 | " Expected: %s " % (dut, interface, grp_addr) | |
4266 | ) | |
4267 | return errormsg | |
4268 | ||
4269 | logger.info( | |
4270 | "[DUT %s]: Verifying local IGMP group %s received " | |
4271 | "from interface %s [PASSED]!! ", | |
4272 | dut, | |
4273 | grp_addr, | |
4274 | interface, | |
4275 | ) | |
4276 | ||
4277 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
4278 | return True | |
4279 | ||
4280 | ||
e13f9c4f | 4281 | def verify_pim_interface_traffic(tgen, input_dict, return_stats=True, addr_type="ipv4"): |
c28e6ef5 | 4282 | """ |
d7032129 | 4283 | Verify ip pim interface traffic by running |
c28e6ef5 | 4284 | "show ip pim interface traffic" cli |
4285 | ||
4286 | Parameters | |
4287 | ---------- | |
4288 | * `tgen`: topogen object | |
4289 | * `input_dict(dict)`: defines DUT, what and from which interfaces | |
4290 | traffic needs to be verified | |
e13f9c4f KK |
4291 | * [optional]`addr_type`: specify address-family, default is ipv4 |
4292 | ||
c28e6ef5 | 4293 | Usage |
4294 | ----- | |
4295 | input_dict = { | |
4296 | "r1": { | |
4297 | "r1-r0-eth0": { | |
4298 | "helloRx": 0, | |
4299 | "helloTx": 1, | |
4300 | "joinRx": 0, | |
4301 | "joinTx": 0 | |
4302 | } | |
4303 | } | |
4304 | } | |
4305 | ||
4306 | result = verify_pim_interface_traffic(tgen, input_dict) | |
4307 | ||
4308 | Returns | |
4309 | ------- | |
4310 | errormsg(str) or True | |
4311 | """ | |
4312 | ||
4313 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
4314 | ||
4315 | output_dict = {} | |
4316 | for dut in input_dict.keys(): | |
4317 | if dut not in tgen.routers(): | |
4318 | continue | |
4319 | ||
4320 | rnode = tgen.routers()[dut] | |
4321 | ||
4322 | logger.info("[DUT: %s]: Verifying pim interface traffic", dut) | |
e13f9c4f KK |
4323 | |
4324 | if addr_type == "ipv4": | |
4325 | cmd = "show ip pim interface traffic json" | |
4326 | elif addr_type == "ipv6": | |
4327 | cmd = "show ipv6 pim interface traffic json" | |
4328 | ||
4329 | show_pim_intf_traffic_json = run_frr_cmd(rnode, cmd, isjson=True) | |
c28e6ef5 | 4330 | |
4331 | output_dict[dut] = {} | |
4332 | for intf, data in input_dict[dut].items(): | |
4333 | interface_json = show_pim_intf_traffic_json[intf] | |
4334 | for state in data: | |
4335 | ||
4336 | # Verify Tx/Rx | |
4337 | if state in interface_json: | |
4338 | output_dict[dut][state] = interface_json[state] | |
4339 | else: | |
4340 | errormsg = ( | |
4341 | "[DUT %s]: %s is not present" | |
4342 | "for interface %s [FAILED]!! " % (dut, state, intf) | |
4343 | ) | |
4344 | return errormsg | |
4345 | ||
4346 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
4347 | return True if return_stats == False else output_dict | |
4348 | ||
d7032129 KK |
4349 | |
4350 | @retry(retry_timeout=40, diag_pct=0) | |
4351 | def verify_mld_groups(tgen, dut, interface, group_addresses, expected=True): | |
4352 | """ | |
4353 | Verify IGMP groups are received from an intended interface | |
4354 | by running "show ip mld groups" command | |
4355 | ||
4356 | Parameters | |
4357 | ---------- | |
4358 | * `tgen`: topogen object | |
4359 | * `dut`: device under test | |
4360 | * `interface`: interface, from which MLD groups would be received | |
4361 | * `group_addresses`: MLD group address | |
4362 | * `expected` : expected results from API, by-default True | |
4363 | ||
4364 | Usage | |
4365 | ----- | |
4366 | dut = "r1" | |
4367 | interface = "r1-r0-eth0" | |
4368 | group_address = "ffaa::1" | |
4369 | result = verify_mld_groups(tgen, dut, interface, group_address) | |
4370 | ||
4371 | Returns | |
4372 | ------- | |
4373 | errormsg(str) or True | |
4374 | """ | |
4375 | ||
4376 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
4377 | ||
4378 | if dut not in tgen.routers(): | |
4379 | return False | |
4380 | ||
4381 | rnode = tgen.routers()[dut] | |
4382 | ||
4383 | logger.info("[DUT: %s]: Verifying mld groups received:", dut) | |
4384 | show_mld_json = run_frr_cmd(rnode, "show ipv6 mld groups json", isjson=True) | |
4385 | ||
4386 | if type(group_addresses) is not list: | |
4387 | group_addresses = [group_addresses] | |
4388 | ||
4389 | if interface in show_mld_json: | |
4390 | show_mld_json = show_mld_json[interface]["groups"] | |
4391 | else: | |
4392 | errormsg = ( | |
4393 | "[DUT %s]: Verifying MLD group received" | |
4394 | " from interface %s [FAILED]!! " % (dut, interface) | |
4395 | ) | |
4396 | return errormsg | |
4397 | ||
4398 | found = False | |
4399 | for grp_addr in group_addresses: | |
4400 | for index in show_mld_json: | |
4401 | if index["group"] == grp_addr: | |
4402 | found = True | |
4403 | break | |
4404 | if found is not True: | |
4405 | errormsg = ( | |
4406 | "[DUT %s]: Verifying MLD group received" | |
4407 | " from interface %s [FAILED]!! " | |
4408 | " Expected not found: %s" % (dut, interface, grp_addr) | |
4409 | ) | |
4410 | return errormsg | |
4411 | ||
4412 | logger.info( | |
4413 | "[DUT %s]: Verifying MLD group %s received " | |
4414 | "from interface %s [PASSED]!! ", | |
4415 | dut, | |
4416 | grp_addr, | |
4417 | interface, | |
4418 | ) | |
4419 | ||
4420 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
4421 | return True | |
4422 | ||
4423 | ||
4424 | @retry(retry_timeout=40, diag_pct=0) | |
4425 | def verify_mld_interface(tgen, dut, mld_iface, interface_ip, expected=True): | |
4426 | """ | |
4427 | Verify all IGMP interface are up and running, config is verified | |
4428 | using "show ip mld interface" cli | |
4429 | ||
4430 | Parameters | |
4431 | ---------- | |
4432 | * `tgen`: topogen object | |
4433 | * `topo` : json file data | |
4434 | * `dut` : device under test | |
4435 | * `mld_iface` : interface name | |
4436 | * `interface_ip` : interface ip address | |
4437 | * `expected` : expected results from API, by-default True | |
4438 | ||
4439 | Usage | |
4440 | ----- | |
4441 | result = verify_mld_interface(tgen, topo, dut, mld_iface, interface_ip) | |
4442 | ||
4443 | Returns | |
4444 | ------- | |
4445 | errormsg(str) or True | |
4446 | """ | |
4447 | ||
4448 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
4449 | ||
4450 | for router in tgen.routers(): | |
4451 | if router != dut: | |
4452 | continue | |
4453 | ||
4454 | logger.info("[DUT: %s]: Verifying MLD interface status:", dut) | |
4455 | ||
4456 | rnode = tgen.routers()[dut] | |
4457 | show_mld_interface_json = run_frr_cmd( | |
4458 | rnode, "show ipv6 mld interface json", isjson=True | |
4459 | ) | |
4460 | ||
4461 | if mld_iface in show_mld_interface_json: | |
4462 | mld_intf_json = show_mld_interface_json[mld_iface] | |
4463 | # Verifying igmp interface | |
4464 | if mld_intf_json["address"] != interface_ip: | |
4465 | errormsg = ( | |
4466 | "[DUT %s]: igmp interface ip is not correct " | |
4467 | "[FAILED]!! Expected : %s, Found : %s" | |
4468 | % (dut, mld_intf_json["address"], interface_ip) | |
4469 | ) | |
4470 | return errormsg | |
4471 | ||
4472 | logger.info( | |
4473 | "[DUT %s]: igmp interface: %s, " "interface ip: %s" " [PASSED]!!", | |
4474 | dut, | |
4475 | mld_iface, | |
4476 | interface_ip, | |
4477 | ) | |
4478 | else: | |
4479 | errormsg = ( | |
4480 | "[DUT %s]: igmp interface: %s " | |
4481 | "igmp interface ip: %s, is not present " | |
4482 | % (dut, mld_iface, interface_ip) | |
4483 | ) | |
4484 | return errormsg | |
4485 | ||
4486 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
4487 | return True | |
4488 | ||
4489 | ||
4490 | @retry(retry_timeout=60, diag_pct=0) | |
4491 | def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): | |
4492 | """ | |
4493 | Verify mld interface details, verifying following configs: | |
4494 | timerQueryInterval | |
4495 | timerQueryResponseIntervalMsec | |
4496 | lastMemberQueryCount | |
4497 | timerLastMemberQueryMsec | |
4498 | ||
4499 | Parameters | |
4500 | ---------- | |
4501 | * `tgen`: topogen object | |
4502 | * `input_dict` : Input dict data, required to verify | |
4503 | timer | |
4504 | * `stats_return`: If user wants API to return statistics | |
4505 | * `expected` : expected results from API, by-default True | |
4506 | ||
4507 | Usage | |
4508 | ----- | |
4509 | input_dict ={ | |
4510 | "l1": { | |
4511 | "mld": { | |
4512 | "interfaces": { | |
4513 | "l1-i1-eth1": { | |
4514 | "mld": { | |
4515 | "query": { | |
4516 | "query-interval" : 200, | |
4517 | "query-max-response-time" : 100 | |
4518 | }, | |
4519 | "statistics": { | |
4520 | "queryV2" : 2, | |
4521 | "reportV2" : 1 | |
4522 | } | |
4523 | } | |
4524 | } | |
4525 | } | |
4526 | } | |
4527 | } | |
4528 | } | |
4529 | result = verify_mld_config(tgen, input_dict, stats_return) | |
4530 | ||
4531 | Returns | |
4532 | ------- | |
4533 | errormsg(str) or True | |
4534 | """ | |
4535 | ||
4536 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
4537 | ||
4538 | for dut in input_dict.keys(): | |
4539 | rnode = tgen.routers()[dut] | |
4540 | ||
4541 | for interface, data in input_dict[dut]["igmp"]["interfaces"].items(): | |
4542 | ||
4543 | statistics = False | |
4544 | report = False | |
4545 | if "statistics" in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"]: | |
4546 | statistics = True | |
4547 | cmd = "show ipv6 mld statistics" | |
4548 | else: | |
4549 | cmd = "show ipv6 mld" | |
4550 | ||
4551 | logger.info("[DUT: %s]: Verifying MLD interface %s detail:", dut, interface) | |
4552 | ||
4553 | if statistics: | |
4554 | if ( | |
4555 | "report" | |
4556 | in input_dict[dut]["mld"]["interfaces"][interface]["mld"][ | |
4557 | "statistics" | |
4558 | ] | |
4559 | ): | |
4560 | report = True | |
4561 | ||
4562 | if statistics and report: | |
4563 | show_ipv6_mld_intf_json = run_frr_cmd( | |
4564 | rnode, "{} json".format(cmd), isjson=True | |
4565 | ) | |
4566 | intf_detail_json = show_ipv6_mld_intf_json["global"] | |
4567 | else: | |
4568 | show_ipv6_mld_intf_json = run_frr_cmd( | |
4569 | rnode, "{} interface {} json".format(cmd, interface), isjson=True | |
4570 | ) | |
4571 | ||
4572 | if not report: | |
4573 | if interface not in show_ipv6_mld_intf_json: | |
4574 | errormsg = ( | |
4575 | "[DUT %s]: MLD interface: %s " | |
4576 | " is not present in CLI output " | |
4577 | "[FAILED]!! " % (dut, interface) | |
4578 | ) | |
4579 | return errormsg | |
4580 | ||
4581 | else: | |
4582 | intf_detail_json = show_ipv6_mld_intf_json[interface] | |
4583 | ||
4584 | if stats_return: | |
4585 | mld_stats = {} | |
4586 | ||
4587 | if "statistics" in data["mld"]: | |
4588 | if stats_return: | |
4589 | mld_stats["statistics"] = {} | |
4590 | for query, value in data["mld"]["statistics"].items(): | |
4591 | if query == "queryV1": | |
4592 | # Verifying IGMP interface queryV2 statistics | |
4593 | if stats_return: | |
4594 | mld_stats["statistics"][query] = intf_detail_json["queryV1"] | |
4595 | ||
4596 | else: | |
4597 | if intf_detail_json["queryV1"] != value: | |
4598 | errormsg = ( | |
4599 | "[DUT %s]: MLD interface: %s " | |
4600 | " queryV1 statistics verification " | |
4601 | "[FAILED]!! Expected : %s," | |
4602 | " Found : %s" | |
4603 | % ( | |
4604 | dut, | |
4605 | interface, | |
4606 | value, | |
4607 | intf_detail_json["queryV1"], | |
4608 | ) | |
4609 | ) | |
4610 | return errormsg | |
4611 | ||
4612 | logger.info( | |
4613 | "[DUT %s]: MLD interface: %s " | |
4614 | "queryV1 statistics is %s", | |
4615 | dut, | |
4616 | interface, | |
4617 | value, | |
4618 | ) | |
4619 | ||
4620 | if query == "reportV1": | |
4621 | # Verifying IGMP interface timerV2 statistics | |
4622 | if stats_return: | |
4623 | mld_stats["statistics"][query] = intf_detail_json[ | |
4624 | "reportV1" | |
4625 | ] | |
4626 | ||
4627 | else: | |
4628 | if intf_detail_json["reportV1"] <= value: | |
4629 | errormsg = ( | |
4630 | "[DUT %s]: MLD reportV1 " | |
4631 | "statistics verification " | |
4632 | "[FAILED]!! Expected : %s " | |
4633 | "or more, Found : %s" | |
4634 | % ( | |
4635 | dut, | |
4636 | interface, | |
4637 | value, | |
4638 | ) | |
4639 | ) | |
4640 | return errormsg | |
4641 | ||
4642 | logger.info( | |
4643 | "[DUT %s]: MLD reportV1 " "statistics is %s", | |
4644 | dut, | |
4645 | intf_detail_json["reportV1"], | |
4646 | ) | |
4647 | ||
4648 | if "query" in data["mld"]: | |
4649 | for query, value in data["mld"]["query"].items(): | |
4650 | if query == "query-interval": | |
4651 | # Verifying IGMP interface query interval timer | |
4652 | if intf_detail_json["timerQueryInterval"] != value: | |
4653 | errormsg = ( | |
4654 | "[DUT %s]: MLD interface: %s " | |
4655 | " query-interval verification " | |
4656 | "[FAILED]!! Expected : %s," | |
4657 | " Found : %s" | |
4658 | % ( | |
4659 | dut, | |
4660 | interface, | |
4661 | value, | |
4662 | intf_detail_json["timerQueryInterval"], | |
4663 | ) | |
4664 | ) | |
4665 | return errormsg | |
4666 | ||
4667 | logger.info( | |
4668 | "[DUT %s]: MLD interface: %s " "query-interval is %s", | |
4669 | dut, | |
4670 | interface, | |
4671 | value, | |
4672 | ) | |
4673 | ||
4674 | if query == "query-max-response-time": | |
4675 | # Verifying IGMP interface query max response timer | |
4676 | if ( | |
4677 | intf_detail_json["timerQueryResponseIntervalMsec"] | |
4678 | != value * 100 | |
4679 | ): | |
4680 | errormsg = ( | |
4681 | "[DUT %s]: MLD interface: %s " | |
4682 | "query-max-response-time " | |
4683 | "verification [FAILED]!!" | |
4684 | " Expected : %s, Found : %s" | |
4685 | % ( | |
4686 | dut, | |
4687 | interface, | |
4688 | value * 1000, | |
4689 | intf_detail_json["timerQueryResponseIntervalMsec"], | |
4690 | ) | |
4691 | ) | |
4692 | return errormsg | |
4693 | ||
4694 | logger.info( | |
4695 | "[DUT %s]: MLD interface: %s " | |
4696 | "query-max-response-time is %s ms", | |
4697 | dut, | |
4698 | interface, | |
4699 | value * 100, | |
4700 | ) | |
4701 | ||
4702 | if query == "last-member-query-count": | |
4703 | # Verifying IGMP interface last member query count | |
4704 | if intf_detail_json["lastMemberQueryCount"] != value: | |
4705 | errormsg = ( | |
4706 | "[DUT %s]: MLD interface: %s " | |
4707 | "last-member-query-count " | |
4708 | "verification [FAILED]!!" | |
4709 | " Expected : %s, Found : %s" | |
4710 | % ( | |
4711 | dut, | |
4712 | interface, | |
4713 | value, | |
4714 | intf_detail_json["lastMemberQueryCount"], | |
4715 | ) | |
4716 | ) | |
4717 | return errormsg | |
4718 | ||
4719 | logger.info( | |
4720 | "[DUT %s]: MLD interface: %s " | |
4721 | "last-member-query-count is %s ms", | |
4722 | dut, | |
4723 | interface, | |
4724 | value * 1000, | |
4725 | ) | |
4726 | ||
4727 | if query == "last-member-query-interval": | |
4728 | # Verifying IGMP interface last member query interval | |
4729 | if ( | |
4730 | intf_detail_json["timerLastMemberQueryMsec"] | |
4731 | != value * 100 * intf_detail_json["lastMemberQueryCount"] | |
4732 | ): | |
4733 | errormsg = ( | |
4734 | "[DUT %s]: MLD interface: %s " | |
4735 | "last-member-query-interval " | |
4736 | "verification [FAILED]!!" | |
4737 | " Expected : %s, Found : %s" | |
4738 | % ( | |
4739 | dut, | |
4740 | interface, | |
4741 | value * 1000, | |
4742 | intf_detail_json["timerLastMemberQueryMsec"], | |
4743 | ) | |
4744 | ) | |
4745 | return errormsg | |
4746 | ||
4747 | logger.info( | |
4748 | "[DUT %s]: MLD interface: %s " | |
4749 | "last-member-query-interval is %s ms", | |
4750 | dut, | |
4751 | interface, | |
4752 | value * intf_detail_json["lastMemberQueryCount"] * 100, | |
4753 | ) | |
4754 | ||
4755 | if "version" in data["mld"]: | |
4756 | # Verifying IGMP interface state is up | |
4757 | if intf_detail_json["state"] != "up": | |
4758 | errormsg = ( | |
4759 | "[DUT %s]: MLD interface: %s " | |
4760 | " state: %s verification " | |
4761 | "[FAILED]!!" % (dut, interface, intf_detail_json["state"]) | |
4762 | ) | |
4763 | return errormsg | |
4764 | ||
4765 | logger.info( | |
4766 | "[DUT %s]: MLD interface: %s " "state: %s", | |
4767 | dut, | |
4768 | interface, | |
4769 | intf_detail_json["state"], | |
4770 | ) | |
4771 | ||
4772 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
4773 | return True if stats_return == False else mld_stats | |
4774 | ||
4775 | ||
4776 | @retry(retry_timeout=60, diag_pct=0) | |
4777 | def verify_pim_nexthop(tgen, topo, dut, nexthop, addr_type): | |
4778 | """ | |
4779 | Verify all PIM nexthop details using "show ip/ipv6 pim neighbor" cli | |
4780 | ||
4781 | Parameters | |
4782 | ---------- | |
4783 | * `tgen`: topogen object | |
4784 | * `topo` : json file data | |
4785 | * `dut` : dut info | |
4786 | * `nexthop` : nexthop ip/ipv6 address | |
4787 | ||
4788 | Usage | |
4789 | ----- | |
4790 | result = verify_pim_nexthop(tgen, topo, dut, nexthop) | |
4791 | ||
4792 | Returns | |
4793 | ------- | |
4794 | errormsg(str) or True | |
4795 | """ | |
4796 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
4797 | ||
4798 | rnode = tgen.routers()[dut] | |
4799 | ||
4800 | if addr_type == "ipv4": | |
4801 | ip_cmd = "ip" | |
4802 | elif addr_type == "ipv6": | |
4803 | ip_cmd = "ipv6" | |
4804 | ||
4805 | cmd = "show {} pim nexthop".format(addr_type) | |
4806 | pim_nexthop = rnode.vtysh_cmd(cmd) | |
4807 | ||
4808 | if nexthop in pim_nexthop: | |
4809 | logger.info("[DUT %s]: Expected nexthop: %s, Found", dut, nexthop) | |
4810 | return True | |
4811 | else: | |
4812 | errormsg = "[DUT %s]: Nexthop not found: %s" % (dut, nexthop) | |
4813 | return errormsg | |
4814 | ||
4815 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
4816 | return True | |
4817 | ||
4818 | ||
4819 | @retry(retry_timeout=60, diag_pct=0) | |
4820 | def verify_mroute_summary( | |
4821 | tgen, dut, sg_mroute=None, starg_mroute=None, total_mroute=None, addr_type="ipv4" | |
4822 | ): | |
4823 | """ | |
4824 | Verify ip mroute summary has correct (*,g) (s,G) and total mroutes | |
4825 | by running "show ip mroutes summary json" cli | |
4826 | ||
4827 | Parameters | |
4828 | ---------- | |
4829 | * `tgen`: topogen object | |
4830 | * `dut`: device under test | |
4831 | * `sg_mroute`: Number of installed (s,g) mroute | |
4832 | * `starg_mroute`: Number installed of (*,g) mroute | |
4833 | * `Total_mroute`: Total number of installed mroutes | |
4834 | * 'addr_type : IPv4 or IPv6 address | |
4835 | * `return_json`: Whether to return raw json data | |
4836 | ||
4837 | Usage | |
4838 | ----- | |
4839 | dut = "r1" | |
4840 | sg_mroute = "4000" | |
4841 | starg_mroute= "2000" | |
4842 | total_mroute = "6000" | |
4843 | addr_type=IPv4 or IPv6 | |
4844 | result = verify_mroute_summary(tgen, dut, sg_mroute=None, starg_mroute=None, | |
4845 | total_mroute= None) | |
4846 | Returns | |
4847 | ------- | |
4848 | errormsg or True | |
4849 | """ | |
4850 | ||
4851 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
4852 | ||
4853 | if dut not in tgen.routers(): | |
4854 | return False | |
4855 | ||
4856 | rnode = tgen.routers()[dut] | |
4857 | ||
4858 | logger.info("[DUT: %s]: Verifying mroute summary", dut) | |
4859 | ||
4860 | if addr_type == "ipv4": | |
4861 | ip_cmd = "ip" | |
4862 | elif addr_type == "ipv6": | |
4863 | ip_cmd = "ipv6" | |
4864 | ||
4865 | cmd = "show {} mroute summary json".format(ip_cmd) | |
4866 | show_mroute_summary_json = run_frr_cmd(rnode, cmd, isjson=True) | |
4867 | ||
4868 | if starg_mroute is not None: | |
4869 | if show_mroute_summary_json["wildcardGroup"]["installed"] != starg_mroute: | |
4870 | logger.error( | |
4871 | "Number of installed starg are: %s but expected: %s", | |
4872 | show_mroute_summary_json["wildcardGroup"]["installed"], | |
4873 | starg_mroute, | |
4874 | ) | |
4875 | return False | |
4876 | logger.info( | |
4877 | "Number of installed starg routes are %s", | |
4878 | show_mroute_summary_json["wildcardGroup"]["installed"], | |
4879 | ) | |
4880 | ||
4881 | if sg_mroute is not None: | |
4882 | if show_mroute_summary_json["sourceGroup"]["installed"] != sg_mroute: | |
4883 | logger.error( | |
4884 | "Number of installed SG routes are: %s but expected: %s", | |
4885 | show_mroute_summary_json["sourceGroup"]["installed"], | |
4886 | sg_mroute, | |
4887 | ) | |
4888 | return False | |
4889 | logger.info( | |
4890 | "Number of installed SG routes are %s", | |
4891 | show_mroute_summary_json["sourceGroup"]["installed"], | |
4892 | ) | |
4893 | ||
4894 | if total_mroute is not None: | |
4895 | if show_mroute_summary_json["totalNumOfInstalledMroutes"] != total_mroute: | |
4896 | logger.error( | |
4897 | "Total number of installed mroutes are: %s but expected: %s", | |
4898 | show_mroute_summary_json["totalNumOfInstalledMroutes"], | |
4899 | total_mroute, | |
4900 | ) | |
4901 | return False | |
4902 | logger.info( | |
4903 | "Number of installed Total mroute are %s", | |
4904 | show_mroute_summary_json["totalNumOfInstalledMroutes"], | |
4905 | ) | |
4906 | ||
4907 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
4908 | return True | |
4909 | ||
4910 | ||
4911 | def verify_sg_traffic(tgen, dut, groups, src, addr_type="ipv4"): | |
4912 | """ | |
4913 | Verify multicast traffic by running | |
4914 | "show ip mroute count json" cli | |
4915 | ||
4916 | Parameters | |
4917 | ---------- | |
4918 | * `tgen`: topogen object | |
4919 | * `groups`: igmp or mld groups where traffic needs to be verified | |
4920 | ||
4921 | Usage | |
4922 | ----- | |
4923 | result = verify_sg_traffic(tgen, "r1", igmp_groups, srcaddress) | |
4924 | ||
4925 | Returns | |
4926 | ------- | |
4927 | errormsg(str) or True | |
4928 | """ | |
4929 | ||
4930 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
4931 | result = False | |
4932 | ||
4933 | rnode = tgen.routers()[dut] | |
4934 | ||
4935 | logger.info("[DUT: %s]: Verifying multicast " "SG traffic", dut) | |
4936 | ||
4937 | if addr_type == "ipv4": | |
4938 | cmd = "show ip mroute count json" | |
4939 | elif addr_type == "ipv6": | |
4940 | cmd = "show ipv6 mroute count json" | |
4941 | # import pdb; pdb.set_trace() | |
4942 | show_mroute_sg_traffic_json = run_frr_cmd(rnode, cmd, isjson=True) | |
4943 | ||
4944 | if bool(show_mroute_sg_traffic_json) is False: | |
4945 | errormsg = "[DUT %s]: Json output is empty" % (dut) | |
4946 | return errormsg | |
4947 | ||
4948 | before_traffic = {} | |
4949 | after_traffic = {} | |
4950 | ||
4951 | for grp in groups: | |
4952 | if grp not in show_mroute_sg_traffic_json: | |
4953 | errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % ( | |
4954 | dut, | |
4955 | src, | |
4956 | grp, | |
4957 | ) | |
4958 | if src not in show_mroute_sg_traffic_json[grp]: | |
4959 | errormsg = ( | |
4960 | "[DUT %s]: Verifying source is not present in " | |
4961 | " %s [FAILED]!! " % (dut, src) | |
4962 | ) | |
4963 | return errormsg | |
4964 | ||
4965 | before_traffic[grp] = show_mroute_sg_traffic_json[grp][src]["packets"] | |
4966 | ||
4967 | logger.info("Waiting for 10sec traffic to increament") | |
4968 | sleep(10) | |
4969 | ||
4970 | show_mroute_sg_traffic_json = run_frr_cmd(rnode, cmd, isjson=True) | |
4971 | ||
4972 | for grp in groups: | |
4973 | if grp not in show_mroute_sg_traffic_json: | |
4974 | errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % ( | |
4975 | dut, | |
4976 | src, | |
4977 | grp, | |
4978 | ) | |
4979 | if src not in show_mroute_sg_traffic_json[grp]: | |
4980 | errormsg = ( | |
4981 | "[DUT %s]: Verifying source is not present in " | |
4982 | " %s [FAILED]!! " % (dut, src) | |
4983 | ) | |
4984 | return errormsg | |
4985 | ||
4986 | after_traffic[grp] = show_mroute_sg_traffic_json[grp][src]["packets"] | |
4987 | ||
4988 | for grp in groups: | |
4989 | if after_traffic[grp] < before_traffic[grp]: | |
4990 | errormsg = ( | |
4991 | "[DUT %s]: Verifying igmp group %s source %s not increamenting traffic" | |
4992 | " [FAILED]!! " % (dut, grp, src) | |
4993 | ) | |
4994 | return errormsg | |
4995 | else: | |
4996 | logger.info( | |
4997 | "[DUT %s]:igmp group %s source %s receiving traffic" | |
4998 | " [PASSED]!! " % (dut, grp, src) | |
4999 | ) | |
5000 | result = True | |
5001 | ||
5002 | return result | |
5003 | ||
a5124c49 CH |
5004 | # def cleanup(self): |
5005 | # super(McastTesterHelper, self).cleanup() | |
5006 | ||
5007 | # if not self.listen_sock: | |
5008 | # return | |
5009 | ||
5010 | # logger.debug("%s: closing listen socket %s", self, self.app_sock_path) | |
5011 | # self.listen_sock.close() | |
5012 | # self.listen_sock = None | |
5013 | ||
5014 | # if os.path.exists(self.app_sock_path): | |
5015 | # os.remove(self.app_sock_path) | |
5016 | ||
5017 | # def started_proc(self, host, p): | |
5018 | # logger.debug("%s: %s: accepting on socket %s", self, host, self.app_sock_path) | |
5019 | # try: | |
5020 | # conn = self.listen_sock.accept() | |
5021 | # return conn | |
5022 | # except Exception as error: | |
5023 | # logger.error("%s: %s: accept on socket failed: %s", self, host, error) | |
5024 | # if p.poll() is not None: | |
5025 | # logger.error("%s: %s: helper app quit: %s", self, host, comm_error(p)) | |
5026 | # raise | |
5027 | ||
5028 | # def stopping_proc(self, host, p, conn): | |
5029 | # logger.debug("%s: %s: closing socket %s", self, host, conn) | |
5030 | # conn[0].close() |