]> git.proxmox.com Git - mirror_frr.git/blame - tests/topotests/lib/pim.py
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / tests / topotests / lib / pim.py
CommitLineData
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 6import datetime
e8cd26fd 7import os
8import re
49581587 9import sys
e8cd26fd 10import traceback
3d7d6e9a 11import functools
e8cd26fd 12from copy import deepcopy
49581587 13from time import sleep
3d7d6e9a 14from lib import topotest
49581587 15
e8cd26fd 16
17# Import common_config to use commomnly used APIs
18from 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 29from lib.micronet import get_exec_path
49581587 30from lib.topolog import logger
a5124c49 31from lib.topotest import frr_unicode
e8cd26fd 32
33####
34CWD = os.path.dirname(os.path.realpath(__file__))
35
36
37def 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 113def _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
250def 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
359def 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 474def _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 576def 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
614def 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 688def 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)
815def 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 943def 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 1017def 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 1213def 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 1341def 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 1557def 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 1744def 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 1870def 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
1943def 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 2017def 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 2151def 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 2185def 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
2219def 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
2253def 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 2344def 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 2424def 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 2524def 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
2551def 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 2580def 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
2656def 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 2701def 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
2751def 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
2818def 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 2923def 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 2979def 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
3090def 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
3134def 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 3179def 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 3290def 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 3580def 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 3706def 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
3878def 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
3949def 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 4046def 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 4111class 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)
4197def 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 4268def 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)
4338def 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)
4412def 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)
4478def 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]
4527
4528 for interface, data in input_dict[dut]["igmp"]["interfaces"].items():
4529
4530 statistics = False
4531 report = False
4532 if "statistics" in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"]:
4533 statistics = True
4534 cmd = "show ipv6 mld statistics"
4535 else:
4536 cmd = "show ipv6 mld"
4537
4538 logger.info("[DUT: %s]: Verifying MLD interface %s detail:", dut, interface)
4539
4540 if statistics:
4541 if (
4542 "report"
4543 in input_dict[dut]["mld"]["interfaces"][interface]["mld"][
4544 "statistics"
4545 ]
4546 ):
4547 report = True
4548
4549 if statistics and report:
4550 show_ipv6_mld_intf_json = run_frr_cmd(
4551 rnode, "{} json".format(cmd), isjson=True
4552 )
4553 intf_detail_json = show_ipv6_mld_intf_json["global"]
4554 else:
4555 show_ipv6_mld_intf_json = run_frr_cmd(
4556 rnode, "{} interface {} json".format(cmd, interface), isjson=True
4557 )
4558
4559 if not report:
4560 if interface not in show_ipv6_mld_intf_json:
4561 errormsg = (
4562 "[DUT %s]: MLD interface: %s "
4563 " is not present in CLI output "
4564 "[FAILED]!! " % (dut, interface)
4565 )
4566 return errormsg
4567
4568 else:
4569 intf_detail_json = show_ipv6_mld_intf_json[interface]
4570
4571 if stats_return:
4572 mld_stats = {}
4573
4574 if "statistics" in data["mld"]:
4575 if stats_return:
4576 mld_stats["statistics"] = {}
4577 for query, value in data["mld"]["statistics"].items():
4578 if query == "queryV1":
4579 # Verifying IGMP interface queryV2 statistics
4580 if stats_return:
4581 mld_stats["statistics"][query] = intf_detail_json["queryV1"]
4582
4583 else:
4584 if intf_detail_json["queryV1"] != value:
4585 errormsg = (
4586 "[DUT %s]: MLD interface: %s "
4587 " queryV1 statistics verification "
4588 "[FAILED]!! Expected : %s,"
4589 " Found : %s"
4590 % (
4591 dut,
4592 interface,
4593 value,
4594 intf_detail_json["queryV1"],
4595 )
4596 )
4597 return errormsg
4598
4599 logger.info(
4600 "[DUT %s]: MLD interface: %s "
4601 "queryV1 statistics is %s",
4602 dut,
4603 interface,
4604 value,
4605 )
4606
4607 if query == "reportV1":
4608 # Verifying IGMP interface timerV2 statistics
4609 if stats_return:
4610 mld_stats["statistics"][query] = intf_detail_json[
4611 "reportV1"
4612 ]
4613
4614 else:
4615 if intf_detail_json["reportV1"] <= value:
4616 errormsg = (
4617 "[DUT %s]: MLD reportV1 "
4618 "statistics verification "
4619 "[FAILED]!! Expected : %s "
4620 "or more, Found : %s"
4621 % (
4622 dut,
4623 interface,
4624 value,
4625 )
4626 )
4627 return errormsg
4628
4629 logger.info(
4630 "[DUT %s]: MLD reportV1 " "statistics is %s",
4631 dut,
4632 intf_detail_json["reportV1"],
4633 )
4634
4635 if "query" in data["mld"]:
4636 for query, value in data["mld"]["query"].items():
4637 if query == "query-interval":
4638 # Verifying IGMP interface query interval timer
4639 if intf_detail_json["timerQueryInterval"] != value:
4640 errormsg = (
4641 "[DUT %s]: MLD interface: %s "
4642 " query-interval verification "
4643 "[FAILED]!! Expected : %s,"
4644 " Found : %s"
4645 % (
4646 dut,
4647 interface,
4648 value,
4649 intf_detail_json["timerQueryInterval"],
4650 )
4651 )
4652 return errormsg
4653
4654 logger.info(
4655 "[DUT %s]: MLD interface: %s " "query-interval is %s",
4656 dut,
4657 interface,
4658 value,
4659 )
4660
4661 if query == "query-max-response-time":
4662 # Verifying IGMP interface query max response timer
4663 if (
4664 intf_detail_json["timerQueryResponseIntervalMsec"]
4665 != value * 100
4666 ):
4667 errormsg = (
4668 "[DUT %s]: MLD interface: %s "
4669 "query-max-response-time "
4670 "verification [FAILED]!!"
4671 " Expected : %s, Found : %s"
4672 % (
4673 dut,
4674 interface,
4675 value * 1000,
4676 intf_detail_json["timerQueryResponseIntervalMsec"],
4677 )
4678 )
4679 return errormsg
4680
4681 logger.info(
4682 "[DUT %s]: MLD interface: %s "
4683 "query-max-response-time is %s ms",
4684 dut,
4685 interface,
4686 value * 100,
4687 )
4688
4689 if query == "last-member-query-count":
4690 # Verifying IGMP interface last member query count
4691 if intf_detail_json["lastMemberQueryCount"] != value:
4692 errormsg = (
4693 "[DUT %s]: MLD interface: %s "
4694 "last-member-query-count "
4695 "verification [FAILED]!!"
4696 " Expected : %s, Found : %s"
4697 % (
4698 dut,
4699 interface,
4700 value,
4701 intf_detail_json["lastMemberQueryCount"],
4702 )
4703 )
4704 return errormsg
4705
4706 logger.info(
4707 "[DUT %s]: MLD interface: %s "
4708 "last-member-query-count is %s ms",
4709 dut,
4710 interface,
4711 value * 1000,
4712 )
4713
4714 if query == "last-member-query-interval":
4715 # Verifying IGMP interface last member query interval
4716 if (
4717 intf_detail_json["timerLastMemberQueryMsec"]
4718 != value * 100 * intf_detail_json["lastMemberQueryCount"]
4719 ):
4720 errormsg = (
4721 "[DUT %s]: MLD interface: %s "
4722 "last-member-query-interval "
4723 "verification [FAILED]!!"
4724 " Expected : %s, Found : %s"
4725 % (
4726 dut,
4727 interface,
4728 value * 1000,
4729 intf_detail_json["timerLastMemberQueryMsec"],
4730 )
4731 )
4732 return errormsg
4733
4734 logger.info(
4735 "[DUT %s]: MLD interface: %s "
4736 "last-member-query-interval is %s ms",
4737 dut,
4738 interface,
4739 value * intf_detail_json["lastMemberQueryCount"] * 100,
4740 )
4741
4742 if "version" in data["mld"]:
4743 # Verifying IGMP interface state is up
4744 if intf_detail_json["state"] != "up":
4745 errormsg = (
4746 "[DUT %s]: MLD interface: %s "
4747 " state: %s verification "
4748 "[FAILED]!!" % (dut, interface, intf_detail_json["state"])
4749 )
4750 return errormsg
4751
4752 logger.info(
4753 "[DUT %s]: MLD interface: %s " "state: %s",
4754 dut,
4755 interface,
4756 intf_detail_json["state"],
4757 )
4758
4759 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
4760 return True if stats_return == False else mld_stats
4761
4762
4763@retry(retry_timeout=60, diag_pct=0)
4764def verify_pim_nexthop(tgen, topo, dut, nexthop, addr_type):
4765 """
4766 Verify all PIM nexthop details using "show ip/ipv6 pim neighbor" cli
4767
4768 Parameters
4769 ----------
4770 * `tgen`: topogen object
4771 * `topo` : json file data
4772 * `dut` : dut info
4773 * `nexthop` : nexthop ip/ipv6 address
4774
4775 Usage
4776 -----
4777 result = verify_pim_nexthop(tgen, topo, dut, nexthop)
4778
4779 Returns
4780 -------
4781 errormsg(str) or True
4782 """
4783 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
4784
4785 rnode = tgen.routers()[dut]
4786
4787 if addr_type == "ipv4":
4788 ip_cmd = "ip"
4789 elif addr_type == "ipv6":
4790 ip_cmd = "ipv6"
4791
4792 cmd = "show {} pim nexthop".format(addr_type)
4793 pim_nexthop = rnode.vtysh_cmd(cmd)
4794
4795 if nexthop in pim_nexthop:
4796 logger.info("[DUT %s]: Expected nexthop: %s, Found", dut, nexthop)
4797 return True
4798 else:
4799 errormsg = "[DUT %s]: Nexthop not found: %s" % (dut, nexthop)
4800 return errormsg
4801
4802 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
4803 return True
4804
4805
4806@retry(retry_timeout=60, diag_pct=0)
4807def verify_mroute_summary(
4808 tgen, dut, sg_mroute=None, starg_mroute=None, total_mroute=None, addr_type="ipv4"
4809):
4810 """
4811 Verify ip mroute summary has correct (*,g) (s,G) and total mroutes
4812 by running "show ip mroutes summary json" cli
4813
4814 Parameters
4815 ----------
4816 * `tgen`: topogen object
4817 * `dut`: device under test
4818 * `sg_mroute`: Number of installed (s,g) mroute
4819 * `starg_mroute`: Number installed of (*,g) mroute
4820 * `Total_mroute`: Total number of installed mroutes
4821 * 'addr_type : IPv4 or IPv6 address
4822 * `return_json`: Whether to return raw json data
4823
4824 Usage
4825 -----
4826 dut = "r1"
4827 sg_mroute = "4000"
4828 starg_mroute= "2000"
4829 total_mroute = "6000"
4830 addr_type=IPv4 or IPv6
4831 result = verify_mroute_summary(tgen, dut, sg_mroute=None, starg_mroute=None,
4832 total_mroute= None)
4833 Returns
4834 -------
4835 errormsg or True
4836 """
4837
4838 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
4839
4840 if dut not in tgen.routers():
4841 return False
4842
4843 rnode = tgen.routers()[dut]
4844
4845 logger.info("[DUT: %s]: Verifying mroute summary", dut)
4846
4847 if addr_type == "ipv4":
4848 ip_cmd = "ip"
4849 elif addr_type == "ipv6":
4850 ip_cmd = "ipv6"
4851
4852 cmd = "show {} mroute summary json".format(ip_cmd)
4853 show_mroute_summary_json = run_frr_cmd(rnode, cmd, isjson=True)
4854
4855 if starg_mroute is not None:
4856 if show_mroute_summary_json["wildcardGroup"]["installed"] != starg_mroute:
4857 logger.error(
4858 "Number of installed starg are: %s but expected: %s",
4859 show_mroute_summary_json["wildcardGroup"]["installed"],
4860 starg_mroute,
4861 )
4862 return False
4863 logger.info(
4864 "Number of installed starg routes are %s",
4865 show_mroute_summary_json["wildcardGroup"]["installed"],
4866 )
4867
4868 if sg_mroute is not None:
4869 if show_mroute_summary_json["sourceGroup"]["installed"] != sg_mroute:
4870 logger.error(
4871 "Number of installed SG routes are: %s but expected: %s",
4872 show_mroute_summary_json["sourceGroup"]["installed"],
4873 sg_mroute,
4874 )
4875 return False
4876 logger.info(
4877 "Number of installed SG routes are %s",
4878 show_mroute_summary_json["sourceGroup"]["installed"],
4879 )
4880
4881 if total_mroute is not None:
4882 if show_mroute_summary_json["totalNumOfInstalledMroutes"] != total_mroute:
4883 logger.error(
4884 "Total number of installed mroutes are: %s but expected: %s",
4885 show_mroute_summary_json["totalNumOfInstalledMroutes"],
4886 total_mroute,
4887 )
4888 return False
4889 logger.info(
4890 "Number of installed Total mroute are %s",
4891 show_mroute_summary_json["totalNumOfInstalledMroutes"],
4892 )
4893
4894 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
4895 return True
4896
4897
4898def verify_sg_traffic(tgen, dut, groups, src, addr_type="ipv4"):
4899 """
4900 Verify multicast traffic by running
4901 "show ip mroute count json" cli
4902
4903 Parameters
4904 ----------
4905 * `tgen`: topogen object
4906 * `groups`: igmp or mld groups where traffic needs to be verified
4907
4908 Usage
4909 -----
4910 result = verify_sg_traffic(tgen, "r1", igmp_groups, srcaddress)
4911
4912 Returns
4913 -------
4914 errormsg(str) or True
4915 """
4916
4917 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
4918 result = False
4919
4920 rnode = tgen.routers()[dut]
4921
4922 logger.info("[DUT: %s]: Verifying multicast " "SG traffic", dut)
4923
4924 if addr_type == "ipv4":
4925 cmd = "show ip mroute count json"
4926 elif addr_type == "ipv6":
4927 cmd = "show ipv6 mroute count json"
4928 # import pdb; pdb.set_trace()
4929 show_mroute_sg_traffic_json = run_frr_cmd(rnode, cmd, isjson=True)
4930
4931 if bool(show_mroute_sg_traffic_json) is False:
4932 errormsg = "[DUT %s]: Json output is empty" % (dut)
4933 return errormsg
4934
4935 before_traffic = {}
4936 after_traffic = {}
4937
4938 for grp in groups:
4939 if grp not in show_mroute_sg_traffic_json:
4940 errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
4941 dut,
4942 src,
4943 grp,
4944 )
4945 if src not in show_mroute_sg_traffic_json[grp]:
4946 errormsg = (
4947 "[DUT %s]: Verifying source is not present in "
4948 " %s [FAILED]!! " % (dut, src)
4949 )
4950 return errormsg
4951
4952 before_traffic[grp] = show_mroute_sg_traffic_json[grp][src]["packets"]
4953
4954 logger.info("Waiting for 10sec traffic to increament")
4955 sleep(10)
4956
4957 show_mroute_sg_traffic_json = run_frr_cmd(rnode, cmd, isjson=True)
4958
4959 for grp in groups:
4960 if grp not in show_mroute_sg_traffic_json:
4961 errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
4962 dut,
4963 src,
4964 grp,
4965 )
4966 if src not in show_mroute_sg_traffic_json[grp]:
4967 errormsg = (
4968 "[DUT %s]: Verifying source is not present in "
4969 " %s [FAILED]!! " % (dut, src)
4970 )
4971 return errormsg
4972
4973 after_traffic[grp] = show_mroute_sg_traffic_json[grp][src]["packets"]
4974
4975 for grp in groups:
4976 if after_traffic[grp] < before_traffic[grp]:
4977 errormsg = (
4978 "[DUT %s]: Verifying igmp group %s source %s not increamenting traffic"
4979 " [FAILED]!! " % (dut, grp, src)
4980 )
4981 return errormsg
4982 else:
4983 logger.info(
4984 "[DUT %s]:igmp group %s source %s receiving traffic"
4985 " [PASSED]!! " % (dut, grp, src)
4986 )
4987 result = True
4988
4989 return result
4990
a5124c49
CH
4991 # def cleanup(self):
4992 # super(McastTesterHelper, self).cleanup()
4993
4994 # if not self.listen_sock:
4995 # return
4996
4997 # logger.debug("%s: closing listen socket %s", self, self.app_sock_path)
4998 # self.listen_sock.close()
4999 # self.listen_sock = None
5000
5001 # if os.path.exists(self.app_sock_path):
5002 # os.remove(self.app_sock_path)
5003
5004 # def started_proc(self, host, p):
5005 # logger.debug("%s: %s: accepting on socket %s", self, host, self.app_sock_path)
5006 # try:
5007 # conn = self.listen_sock.accept()
5008 # return conn
5009 # except Exception as error:
5010 # logger.error("%s: %s: accept on socket failed: %s", self, host, error)
5011 # if p.poll() is not None:
5012 # logger.error("%s: %s: helper app quit: %s", self, host, comm_error(p))
5013 # raise
5014
5015 # def stopping_proc(self, host, p, conn):
5016 # logger.debug("%s: %s: closing socket %s", self, host, conn)
5017 # conn[0].close()