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