]> git.proxmox.com Git - mirror_frr.git/blame_incremental - tests/topotests/lib/pim.py
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / tests / topotests / lib / pim.py
... / ...
CommitLineData
1# SPDX-License-Identifier: ISC
2# Copyright (c) 2019 by VMware, Inc. ("VMware")
3# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
4# ("NetDEF") in this file.
5
6import datetime
7import os
8import re
9import sys
10import traceback
11import functools
12from copy import deepcopy
13from time import sleep
14from lib import topotest
15
16
17# Import common_config to use commomnly used APIs
18from lib.common_config import (
19 create_common_configurations,
20 HostApplicationHelper,
21 InvalidCLIError,
22 create_common_configuration,
23 InvalidCLIError,
24 retry,
25 run_frr_cmd,
26 validate_ip_address,
27 get_frr_ipv6_linklocal,
28)
29from lib.micronet import get_exec_path
30from lib.topolog import logger
31from lib.topotest import frr_unicode
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 """
39 API to configure pim/pim6 on router
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": {
54 "join-prune-interval": "5",
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 }]
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 }]
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)
88
89 config_data_dict = {}
90
91 for router in input_dict.keys():
92 config_data = _enable_disable_pim_config(tgen, topo, input_dict, router, build)
93
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():
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)
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
108
109 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
110 return result
111
112
113def _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict):
114 """
115 Helper API to create pim RP configurations.
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.
124 * `config_data_dict` : OUT: adds `router` config to dictinary
125 Returns
126 -------
127 None
128 """
129
130 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
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"]
139
140 # pim6
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"]
146
147 # Configure this RP on every router.
148 for dut in tgen.routers():
149 # At least one interface must be enabled for PIM on the router
150 pim_if_enabled = False
151 pim6_if_enabled = False
152 for destLink, data in topo[dut]["links"].items():
153 if "pim" in data:
154 pim_if_enabled = True
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:
160 continue
161
162 config_data = []
163
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 )
173
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 )
188
189 return False
190 group_addr_range = rp_dict.setdefault("group_addr_range", None)
191
192 # Group prefix-list filter
193 prefix_list = rp_dict.setdefault("prefix_list", None)
194
195 # Delete rp config
196 del_action = rp_dict.setdefault("delete", False)
197
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)
206 if del_action:
207 cmd = "no {}".format(cmd)
208 config_data.append(cmd)
209
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]
214
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)
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)
294
295 config_data_dict = {}
296
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)
313 del_attr = intf_data[intf_name]["igmp"].setdefault("delete_attr", False)
314 cmd = "ip igmp"
315 if del_action:
316 cmd = "no {}".format(cmd)
317 if not del_attr:
318 config_data.append(cmd)
319
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)
325 if not del_attr:
326 config_data.append(cmd)
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)
344 if config_data:
345 config_data_dict[router] = config_data
346
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
354
355 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
356 return result
357
358
359def create_mld_config(tgen, topo, input_dict=None, build=False):
360 """
361 API to configure mld for pim6 on router
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
474def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False):
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 -------
488 list of config
489 """
490
491 config_data = []
492
493 # Enable pim/pim6 on interfaces
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
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
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
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
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 [
548 "join-prune-interval",
549 "keep-alive-timer",
550 "register-suppress-time",
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)
556 config_data.append(cmd)
557
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
573 return config_data
574
575
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))
646
647 try:
648 config_data_dict = {}
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
656 config_data = []
657 if "force_expire" in pim_data:
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
670 if config_data:
671 config_data_dict[dut] = config_data
672
673 result = create_common_configurations(
674 tgen, config_data_dict, "pim", build=build
675 )
676 except InvalidCLIError:
677 logger.error("configure_pim_force_expire", exc_info=True)
678 result = False
679
680 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
681 return result
682
683
684#############################################
685# Verification APIs
686#############################################
687@retry(retry_timeout=12)
688def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None, expected=True):
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
699 * `nbr_ip` : neighbor ip of interface
700 * `expected` : expected results from API, by-default True
701
702 Usage
703 -----
704 result = verify_pim_neighbors(tgen, topo, dut, iface=ens192, nbr_ip=20.1.1.2)
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
732 if "pim" in data and data["pim"] == "disable":
733 continue
734
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
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
942@retry(retry_timeout=40, diag_pct=0)
943def verify_igmp_groups(tgen, dut, interface, group_addresses, expected=True):
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
954 * `expected` : expected results from API, by-default True
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
1016@retry(retry_timeout=60, diag_pct=2)
1017def verify_upstream_iif(
1018 tgen,
1019 dut,
1020 iif,
1021 src_address,
1022 group_addresses,
1023 joinState=None,
1024 regState=None,
1025 refCount=1,
1026 addr_type="ipv4",
1027 expected=True,
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
1042 * `expected` : expected results from API, by-default True
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 """
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(
1065 "[DUT: %s]: Verifying upstream Inbound Interface"
1066 " for IGMP/MLD groups received:",
1067 dut,
1068 )
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
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
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 "
1126 "(Inbound Interface) and joinState "
1127 "for (%s, %s), Expected iif: %s, "
1128 "Found iif : %s, and Expected "
1129 "joinState :%s , Found joinState: %s"
1130 % (
1131 dut,
1132 src_address,
1133 grp_addr,
1134 in_interface,
1135 group_addr_json[src_address]["inboundInterface"],
1136 joinState,
1137 group_addr_json[src_address]["joinState"],
1138 )
1139 )
1140 return errormsg
1141
1142 elif group_addr_json[src_address]["joinState"] != joinState:
1143 errormsg = (
1144 "[DUT %s]: Verifying iif "
1145 "(Inbound Interface) and joinState "
1146 "for (%s, %s), Expected iif: %s, "
1147 "Found iif : %s, and Expected "
1148 "joinState :%s , Found joinState: %s"
1149 % (
1150 dut,
1151 src_address,
1152 grp_addr,
1153 in_interface,
1154 group_addr_json[src_address]["inboundInterface"],
1155 joinState,
1156 group_addr_json[src_address]["joinState"],
1157 )
1158 )
1159 return errormsg
1160
1161 if regState:
1162 if group_addr_json[src_address]["regState"] != regState:
1163 errormsg = (
1164 "[DUT %s]: Verifying iif "
1165 "(Inbound Interface) and regState "
1166 "for (%s, %s), Expected iif: %s, "
1167 "Found iif : %s, and Expected "
1168 "regState :%s , Found regState: %s"
1169 % (
1170 dut,
1171 src_address,
1172 grp_addr,
1173 in_interface,
1174 group_addr_json[src_address]["inboundInterface"],
1175 regState,
1176 group_addr_json[src_address]["regState"],
1177 )
1178 )
1179 return errormsg
1180
1181 logger.info(
1182 "[DUT %s]: Verifying iif(Inbound Interface)"
1183 " for (%s,%s) and joinState is %s regstate is %s [PASSED]!! "
1184 " Found Expected: (%s)",
1185 dut,
1186 src_address,
1187 grp_addr,
1188 group_addr_json[src_address]["joinState"],
1189 group_addr_json[src_address]["regState"],
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
1208 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1209 return True
1210
1211
1212@retry(retry_timeout=12)
1213def verify_join_state_and_timer(
1214 tgen, dut, iif, src_address, group_addresses, addr_type="ipv4", expected=True
1215):
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
1227 * `expected` : expected results from API, by-default True
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 )
1253
1254 if type(group_addresses) is not list:
1255 group_addresses = [group_addresses]
1256
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
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]!! "
1313 " Expected: %s, Found: %s"
1314 ) % (
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
1340@retry(retry_timeout=120, diag_pct=0)
1341def verify_mroutes(
1342 tgen,
1343 dut,
1344 src_address,
1345 group_addresses,
1346 iif,
1347 oil,
1348 return_uptime=False,
1349 mwait=0,
1350 addr_type="ipv4",
1351 expected=True,
1352):
1353 """
1354 Verify ip mroutes and make sure (*, G)/(S, G) is present in mroutes
1355 by running "show ip/ipv6 mroute" cli
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
1367 * `expected` : expected results from API, by-default True
1368
1369 Usage
1370 -----
1371 dut = "r1"
1372 group_address = "225.1.1.1"
1373 result = verify_mroutes(tgen, dut, src_address, group_address)
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
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
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)
1409 show_ip_mroute_json = run_frr_cmd(
1410 rnode, "show {} mroute json".format(ip_cmd), isjson=True
1411 )
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
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
1556@retry(retry_timeout=60, diag_pct=0)
1557def verify_pim_rp_info(
1558 tgen,
1559 topo,
1560 dut,
1561 group_addresses,
1562 oif=None,
1563 rp=None,
1564 source=None,
1565 iamrp=None,
1566 addr_type="ipv4",
1567 expected=True,
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
1582 * `expected` : expected results from API, by-default True
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
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
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
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:
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 )
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
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
1644 if rp not in show_ip_rp_info_json:
1645 errormsg = (
1646 "[DUT %s]: Verifying rp-info "
1647 "for rp_address %s [FAILED]!! " % (dut, rp)
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:
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
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
1743@retry(retry_timeout=60, diag_pct=0)
1744def verify_pim_state(
1745 tgen,
1746 dut,
1747 iif,
1748 oil,
1749 group_addresses,
1750 src_address=None,
1751 installed_fl=None,
1752 addr_type="ipv4",
1753 expected=True,
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
1767 * `expected` : expected results from API, by-default True
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)
1790
1791 if type(group_addresses) is not list:
1792 group_addresses = [group_addresses]
1793
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
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
1817 if pim_state_json["installed"] == installed_fl:
1818 logger.info(
1819 "[DUT %s]: group %s is installed flag: %s",
1820 dut,
1821 grp_addr,
1822 pim_state_json["installed"],
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: "
1849 "(iif: %s, oil: %s, installed: %s) "
1850 % (dut, grp_addr, iif, oil, "1"),
1851 "Found: (iif: %s, oil: %s, installed: %s)"
1852 % (
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
1870def get_pim_interface_traffic(tgen, input_dict):
1871 """
1872 get ip pim interface traffic by running
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
1879 traffic needs to be retrieved
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
1893 result = get_pim_interface_traffic(tgen, input_dict)
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)
1910
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 )
1915
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
1938
1939 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1940 return output_dict
1941
1942
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
2016@retry(retry_timeout=40, diag_pct=0)
2017def verify_pim_interface(
2018 tgen, topo, dut, interface=None, interface_ip=None, addr_type="ipv4", expected=True
2019):
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
2029 * `interface` : interface name
2030 * `interface_ip` : interface ip address
2031 * `expected` : expected results from API, by-default True
2032
2033 Usage
2034 -----
2035 result = verify_pim_interfacetgen, topo, dut, interface=ens192, interface_ip=20.1.1.1)
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]
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
2060 )
2061
2062 logger.info("show_pim_interface_json: \n %s", show_pim_interface_json)
2063
2064 if interface_ip:
2065 if interface in show_pim_interface_json:
2066 pim_intf_json = show_pim_interface_json[interface]
2067 if pim_intf_json["address"] != interface_ip:
2068 errormsg = (
2069 "[DUT %s]: %s interface "
2070 "%s is not correct "
2071 "[FAILED]!! Expected : %s, Found : %s"
2072 % (
2073 dut,
2074 pim_cmd,
2075 addr_cmd,
2076 pim_intf_json["address"],
2077 interface_ip,
2078 )
2079 )
2080 return errormsg
2081 else:
2082 logger.info(
2083 "[DUT %s]: %s interface "
2084 "%s is correct "
2085 "[Passed]!! Expected : %s, Found : %s"
2086 % (
2087 dut,
2088 pim_cmd,
2089 addr_cmd,
2090 pim_intf_json["address"],
2091 interface_ip,
2092 )
2093 )
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
2099
2100 if pim_cmd in data and data[pim_cmd] == "enable":
2101 pim_interface = data["interface"]
2102 pim_intf_ip = data[addr_type].split("/")[0]
2103
2104 if pim_interface in show_pim_interface_json:
2105 pim_intf_json = show_pim_interface_json[pim_interface]
2106 else:
2107 errormsg = (
2108 "[DUT %s]: %s interface: %s "
2109 "PIM interface %s: %s, not Found"
2110 % (dut, pim_cmd, pim_interface, addr_cmd, pim_intf_ip)
2111 )
2112 return errormsg
2113
2114 # Verifying PIM interface
2115 if (
2116 pim_intf_json["address"] != pim_intf_ip
2117 and pim_intf_json["state"] != "up"
2118 ):
2119 errormsg = (
2120 "[DUT %s]: %s interface: %s "
2121 "PIM interface %s: %s, status check "
2122 "[FAILED]!! Expected : %s, Found : %s"
2123 % (
2124 dut,
2125 pim_cmd,
2126 pim_interface,
2127 addr_cmd,
2128 pim_intf_ip,
2129 pim_interface,
2130 pim_intf_json["state"],
2131 )
2132 )
2133 return errormsg
2134
2135 logger.info(
2136 "[DUT %s]: %s interface: %s, "
2137 "interface %s: %s, status: %s"
2138 " [PASSED]!!",
2139 dut,
2140 pim_cmd,
2141 pim_interface,
2142 addr_cmd,
2143 pim_intf_ip,
2144 pim_intf_json["state"],
2145 )
2146
2147 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
2148 return True
2149
2150
2151def clear_pim_interface_traffic(tgen, topo):
2152 """
2153 Clear ip pim interface traffic by running
2154 "clear ip pim interface traffic" cli
2155
2156 Parameters
2157 ----------
2158 * `tgen`: topogen object
2159 Usage
2160 -----
2161
2162 result = clear_pim_interface_traffic(tgen, topo)
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
2185def clear_pim6_interface_traffic(tgen, topo):
2186 """
2187 Clear ipv6 pim interface traffic by running
2188 "clear ipv6 pim interface traffic" cli
2189
2190 Parameters
2191 ----------
2192 * `tgen`: topogen object
2193 Usage
2194 -----
2195
2196 result = clear_pim6_interface_traffic(tgen, topo)
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
2205 for dut in tgen.routers():
2206 if "pim" not in topo["routers"][dut]:
2207 continue
2208
2209 rnode = tgen.routers()[dut]
2210
2211 logger.info("[DUT: %s]: Clearing pim6 interface traffic", dut)
2212 result = run_frr_cmd(rnode, "clear ipv6 pim interface traffic")
2213
2214 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
2215
2216 return True
2217
2218
2219def clear_pim6_interfaces(tgen, topo):
2220 """
2221 Clear ipv6 pim interface by running
2222 "clear ipv6 pim interface" cli
2223
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
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
2344def clear_igmp_interfaces(tgen, dut):
2345 """
2346 Clear ip/ipv6 igmp interfaces by running
2347 "clear ip/ipv6 igmp interfaces" cli
2348
2349 Parameters
2350 ----------
2351 * `tgen`: topogen object
2352 * `dut`: device under test
2353
2354 Usage
2355 -----
2356 dut = "r1"
2357 result = clear_igmp_interfaces(tgen, dut)
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
2423@retry(retry_timeout=20)
2424def clear_mroute_verify(tgen, dut, expected=True):
2425 """
2426 Clear ip/ipv6 mroute by running "clear ip/ipv6 mroute" cli and verify
2427 mroutes are up again after mroute clear
2428
2429 Parameters
2430 ----------
2431 * `tgen`: topogen object
2432 * `dut`: Device Under Test
2433 * `expected` : expected results from API, by-default True
2434
2435 Usage
2436 -----
2437
2438 result = clear_mroute_verify(tgen, dut)
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
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
2524def clear_mroute(tgen, dut=None):
2525 """
2526 Clear ip/ipv6 mroute by running "clear ip mroute" cli
2527
2528 Parameters
2529 ----------
2530 * `tgen`: topogen object
2531 * `dut`: device under test, default None
2532
2533 Usage
2534 -----
2535 clear_mroute(tgen, dut)
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
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
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
2684 # Why not config just once, why per group?
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
2697 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
2698 return result
2699
2700
2701def scapy_send_bsr_raw_packet(tgen, topo, senderRouter, receiverRouter, packet=None):
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
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
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]
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
2736 cmd = [
2737 python3_path,
2738 script_path,
2739 packet,
2740 sender_interface,
2741 "--interval=1",
2742 "--count=1",
2743 ]
2744 logger.info("Scapy cmd: \n %s", cmd)
2745 node.cmd_raises(cmd)
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
2817@retry(retry_timeout=12)
2818def verify_pim_grp_rp_source(
2819 tgen, topo, dut, grp_addr, rp_source, rpadd=None, expected=True
2820):
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
2832 * `expected` : expected results from API, by-default True
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
2922@retry(retry_timeout=60, diag_pct=0)
2923def verify_pim_bsr(tgen, topo, dut, bsr_ip, expected=True):
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
2934 * `expected` : expected results from API, by-default True
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
2978@retry(retry_timeout=60, diag_pct=0)
2979def verify_pim_upstream_rpf(
2980 tgen, topo, dut, interface, group_addresses, rp=None, expected=True
2981):
2982 """
2983 Verify IP/IPv6 PIM upstream rpf, config is verified
2984 using "show ip/ipv6 pim neighbor" cli
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
2995 * `expected` : expected results from API, by-default True
2996
2997 Usage
2998 -----
2999 result = verify_pim_upstream_rpf(gen, topo, dut, interface,
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:
3037 rp_details = {dut: rp}
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
3178@retry(retry_timeout=60, diag_pct=0)
3179def verify_pim_join(
3180 tgen,
3181 topo,
3182 dut,
3183 interface,
3184 group_addresses,
3185 src_address=None,
3186 addr_type="ipv4",
3187 expected=True,
3188):
3189 """
3190 Verify ip/ipv6 pim join by running "show ip/ipv6 pim join" cli
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
3200 * `expected` : expected results from API, by-default True
3201
3202 Usage
3203 -----
3204 dut = "r1"
3205 interface = "r1-r0-eth0"
3206 group_address = "225.1.1.1"
3207 result = verify_pim_join(tgen, dut, star, group_address, interface)
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)
3221
3222 if type(group_addresses) is not list:
3223 group_addresses = [group_addresses]
3224
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
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
3289@retry(retry_timeout=60, diag_pct=0)
3290def verify_igmp_config(tgen, input_dict, stats_return=False, expected=True):
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
3304 * `expected` : expected results from API, by-default True
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(
3365 rnode, "{} json".format(cmd), isjson=True
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,
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
3579@retry(retry_timeout=60, diag_pct=0)
3580def verify_pim_config(tgen, input_dict, expected=True):
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
3594 * `expected` : expected results from API, by-default True
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
3705@retry(retry_timeout=20, diag_pct=0)
3706def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=True):
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
3717 * `expected` : expected results from API, by-default True
3718
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
3948@retry(retry_timeout=40, diag_pct=0)
3949def verify_multicast_flag_state(
3950 tgen, dut, src_address, group_addresses, flag, expected=True
3951):
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
3963 * `expected` : expected results from API, by-default True
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
4043
4044
4045@retry(retry_timeout=40, diag_pct=0)
4046def verify_igmp_interface(tgen, dut, igmp_iface, interface_ip, expected=True):
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
4058 * `expected` : expected results from API, by-default True
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
4075 logger.info("[DUT: %s]: Verifying PIM interface status:", dut)
4076
4077 rnode = tgen.routers()[dut]
4078 show_ip_igmp_interface_json = run_frr_cmd(
4079 rnode, "show ip igmp interface json", isjson=True
4080 )
4081
4082 if igmp_iface in show_ip_igmp_interface_json:
4083 igmp_intf_json = show_ip_igmp_interface_json[igmp_iface]
4084 # Verifying igmp interface
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 )
4091 return errormsg
4092
4093 logger.info(
4094 "[DUT %s]: igmp interface: %s, " "interface ip: %s" " [PASSED]!!",
4095 dut,
4096 igmp_iface,
4097 interface_ip,
4098 )
4099 else:
4100 errormsg = (
4101 "[DUT %s]: igmp interface: %s "
4102 "igmp interface ip: %s, is not present "
4103 % (dut, igmp_iface, interface_ip)
4104 )
4105 return errormsg
4106
4107 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
4108 return True
4109
4110
4111class McastTesterHelper(HostApplicationHelper):
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]
4134 [python3_path, self.script_path],
4135 )
4136
4137 def __str__(self):
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:
4157 join_intf = frr_unicode(
4158 self.tgen.json_topo["routers"][host]["links"][join_towards]["interface"]
4159 )
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:
4181 bind_intf = frr_unicode(
4182 self.tgen.json_topo["routers"][host]["links"][bind_towards]["interface"]
4183 )
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
4195
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
4268def verify_pim_interface_traffic(tgen, input_dict, return_stats=True, addr_type="ipv4"):
4269 """
4270 Verify ip pim interface traffic by running
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
4278 * [optional]`addr_type`: specify address-family, default is ipv4
4279
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)
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)
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
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
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()