]> git.proxmox.com Git - mirror_frr.git/blame - tests/topotests/lib/pim.py
Merge pull request #10286 from ton31337/fix/rfc6938
[mirror_frr.git] / tests / topotests / lib / pim.py
CommitLineData
e8cd26fd 1# Copyright (c) 2019 by VMware, Inc. ("VMware")
2# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
3# ("NetDEF") in this file.
4#
5# Permission to use, copy, modify, and/or distribute this software
6# for any purpose with or without fee is hereby granted, provided
7# that the above copyright notice and this permission notice appear
8# in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
14# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
15# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
16# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17# OF THIS SOFTWARE.
18
49581587 19import datetime
e8cd26fd 20import os
21import re
49581587 22import sys
e8cd26fd 23import traceback
3d7d6e9a 24import functools
e8cd26fd 25from copy import deepcopy
49581587 26from time import sleep
3d7d6e9a 27from lib import topotest
49581587 28
e8cd26fd 29
30# Import common_config to use commomnly used APIs
31from lib.common_config import (
4f99894d 32 create_common_configurations,
a5124c49
CH
33 HostApplicationHelper,
34 InvalidCLIError,
49581587 35 create_common_configuration,
e8cd26fd 36 InvalidCLIError,
37 retry,
38 run_frr_cmd,
39)
4953ca97 40from lib.micronet import get_exec_path
49581587 41from lib.topolog import logger
a5124c49 42from lib.topotest import frr_unicode
e8cd26fd 43
44####
45CWD = os.path.dirname(os.path.realpath(__file__))
46
47
48def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True):
49 """
50 API to configure pim on router
51
52 Parameters
53 ----------
54 * `tgen` : Topogen object
55 * `topo` : json file data
56 * `input_dict` : Input dict data, required when configuring from
57 testcase
58 * `build` : Only for initial setup phase this is set as True.
59
60 Usage
61 -----
62 input_dict = {
63 "r1": {
64 "pim": {
0a76e764 65 "join-prune-interval": "5",
e8cd26fd 66 "rp": [{
67 "rp_addr" : "1.0.3.17".
68 "keep-alive-timer": "100"
69 "group_addr_range": ["224.1.1.0/24", "225.1.1.0/24"]
70 "prefix-list": "pf_list_1"
71 "delete": True
72 }]
73 }
74 }
75 }
76
77
78 Returns
79 -------
80 True or False
81 """
82 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
83 result = False
84 if not input_dict:
85 input_dict = deepcopy(topo)
86 else:
87 topo = topo["routers"]
88 input_dict = deepcopy(input_dict)
4f99894d
CH
89
90 config_data_dict = {}
91
e8cd26fd 92 for router in input_dict.keys():
4f99894d 93 config_data = _enable_disable_pim_config(tgen, topo, input_dict, router, build)
e8cd26fd 94
4f99894d
CH
95 if config_data:
96 config_data_dict[router] = config_data
97
98 # Now add RP config to all routers
99 for router in input_dict.keys():
e8cd26fd 100 if "pim" not in input_dict[router]:
e8cd26fd 101 continue
4f99894d
CH
102 if "rp" not in input_dict[router]["pim"]:
103 continue
a53c08bc 104 _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict)
e8cd26fd 105
4f99894d
CH
106 try:
107 result = create_common_configurations(
108 tgen, config_data_dict, "pim", build, load_config
109 )
110 except InvalidCLIError:
111 logger.error("create_pim_config", exc_info=True)
112 result = False
e8cd26fd 113
114 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
115 return result
116
117
4f99894d 118def _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict):
e8cd26fd 119 """
0a76e764 120 Helper API to create pim RP configurations.
e8cd26fd 121
122 Parameters
123 ----------
124 * `tgen` : Topogen object
125 * `topo` : json file data
126 * `input_dict` : Input dict data, required when configuring from testcase
127 * `router` : router id to be configured.
128 * `build` : Only for initial setup phase this is set as True.
4f99894d 129 * `config_data_dict` : OUT: adds `router` config to dictinary
e8cd26fd 130 Returns
131 -------
4f99894d 132 None
e8cd26fd 133 """
134
e8cd26fd 135 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
e8cd26fd 136
0a76e764
CH
137 pim_data = input_dict[router]["pim"]
138 rp_data = pim_data["rp"]
e8cd26fd 139
0a76e764
CH
140 # Configure this RP on every router.
141 for dut in tgen.routers():
0a76e764
CH
142 # At least one interface must be enabled for PIM on the router
143 pim_if_enabled = False
144 for destLink, data in topo[dut]["links"].items():
145 if "pim" in data:
146 pim_if_enabled = True
147 if not pim_if_enabled:
148 continue
e8cd26fd 149
0a76e764 150 config_data = []
e8cd26fd 151
0a76e764
CH
152 for rp_dict in deepcopy(rp_data):
153 # ip address of RP
154 if "rp_addr" not in rp_dict and build:
155 logger.error(
a53c08bc 156 "Router %s: 'ip address of RP' not " "present in input_dict/JSON",
0a76e764
CH
157 router,
158 )
e8cd26fd 159
0a76e764
CH
160 return False
161 rp_addr = rp_dict.setdefault("rp_addr", None)
e8cd26fd 162
0a76e764
CH
163 # Keep alive Timer
164 keep_alive_timer = rp_dict.setdefault("keep_alive_timer", None)
e8cd26fd 165
0a76e764
CH
166 # Group Address range to cover
167 if "group_addr_range" not in rp_dict and build:
168 logger.error(
169 "Router %s:'Group Address range to cover'"
170 " not present in input_dict/JSON",
171 router,
172 )
e8cd26fd 173
0a76e764
CH
174 return False
175 group_addr_range = rp_dict.setdefault("group_addr_range", None)
e8cd26fd 176
0a76e764
CH
177 # Group prefix-list filter
178 prefix_list = rp_dict.setdefault("prefix_list", None)
e8cd26fd 179
0a76e764
CH
180 # Delete rp config
181 del_action = rp_dict.setdefault("delete", False)
e8cd26fd 182
0a76e764
CH
183 if keep_alive_timer:
184 cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer)
185 if del_action:
186 cmd = "no {}".format(cmd)
187 config_data.append(cmd)
e8cd26fd 188
0a76e764
CH
189 if rp_addr:
190 if group_addr_range:
191 if type(group_addr_range) is not list:
192 group_addr_range = [group_addr_range]
193
194 for grp_addr in group_addr_range:
195 cmd = "ip pim rp {} {}".format(rp_addr, grp_addr)
e8cd26fd 196 if del_action:
197 cmd = "no {}".format(cmd)
0a76e764 198 config_data.append(cmd)
e8cd26fd 199
0a76e764 200 if prefix_list:
a53c08bc 201 cmd = "ip pim rp {} prefix-list {}".format(rp_addr, prefix_list)
0a76e764
CH
202 if del_action:
203 cmd = "no {}".format(cmd)
204 config_data.append(cmd)
e8cd26fd 205
4f99894d
CH
206 if config_data:
207 if dut not in config_data_dict:
208 config_data_dict[dut] = config_data
209 else:
210 config_data_dict[dut].extend(config_data)
e8cd26fd 211
212
213def create_igmp_config(tgen, topo, input_dict=None, build=False):
214 """
215 API to configure igmp on router
216
217 Parameters
218 ----------
219 * `tgen` : Topogen object
220 * `topo` : json file data
221 * `input_dict` : Input dict data, required when configuring from
222 testcase
223 * `build` : Only for initial setup phase this is set as True.
224
225 Usage
226 -----
227 input_dict = {
228 "r1": {
229 "igmp": {
230 "interfaces": {
231 "r1-r0-eth0" :{
232 "igmp":{
233 "version": "2",
234 "delete": True
235 "query": {
236 "query-interval" : 100,
237 "query-max-response-time": 200
238 }
239 }
240 }
241 }
242 }
243 }
244 }
245
246 Returns
247 -------
248 True or False
249 """
250 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
251 result = False
252 if not input_dict:
253 input_dict = deepcopy(topo)
254 else:
255 topo = topo["routers"]
256 input_dict = deepcopy(input_dict)
4f99894d
CH
257
258 config_data_dict = {}
259
e8cd26fd 260 for router in input_dict.keys():
261 if "igmp" not in input_dict[router]:
262 logger.debug("Router %s: 'igmp' is not present in " "input_dict", router)
263 continue
264
265 igmp_data = input_dict[router]["igmp"]
266
267 if "interfaces" in igmp_data:
268 config_data = []
269 intf_data = igmp_data["interfaces"]
270
271 for intf_name in intf_data.keys():
272 cmd = "interface {}".format(intf_name)
273 config_data.append(cmd)
274 protocol = "igmp"
275 del_action = intf_data[intf_name]["igmp"].setdefault("delete", False)
276 cmd = "ip igmp"
277 if del_action:
278 cmd = "no {}".format(cmd)
279 config_data.append(cmd)
280
281 del_attr = intf_data[intf_name]["igmp"].setdefault("delete_attr", False)
282 for attribute, data in intf_data[intf_name]["igmp"].items():
283 if attribute == "version":
284 cmd = "ip {} {} {}".format(protocol, attribute, data)
285 if del_action:
286 cmd = "no {}".format(cmd)
287 config_data.append(cmd)
288
289 if attribute == "join":
290 for group in data:
291 cmd = "ip {} {} {}".format(protocol, attribute, group)
292 if del_attr:
293 cmd = "no {}".format(cmd)
294 config_data.append(cmd)
295
296 if attribute == "query":
297 for query, value in data.items():
298 if query != "delete":
299 cmd = "ip {} {} {}".format(protocol, query, value)
300
301 if "delete" in intf_data[intf_name][protocol]["query"]:
302 cmd = "no {}".format(cmd)
303
304 config_data.append(cmd)
4f99894d
CH
305 if config_data:
306 config_data_dict[router] = config_data
e8cd26fd 307
4f99894d
CH
308 try:
309 result = create_common_configurations(
310 tgen, config_data_dict, "interface_config", build=build
311 )
312 except InvalidCLIError:
313 logger.error("create_igmp_config", exc_info=True)
314 result = False
e8cd26fd 315
316 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
317 return result
318
319
4f99894d 320def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False):
e8cd26fd 321 """
322 Helper API to enable or disable pim on interfaces
323
324 Parameters
325 ----------
326 * `tgen` : Topogen object
327 * `topo` : json file data
328 * `input_dict` : Input dict data, required when configuring from testcase
329 * `router` : router id to be configured.
330 * `build` : Only for initial setup phase this is set as True.
331
332 Returns
333 -------
4f99894d 334 list of config
e8cd26fd 335 """
e8cd26fd 336
4f99894d 337 config_data = []
e8cd26fd 338
4f99894d
CH
339 # Enable pim on interfaces
340 for destRouterLink, data in sorted(topo[router]["links"].items()):
341 if "pim" in data and data["pim"] == "enable":
342 # Loopback interfaces
343 if "type" in data and data["type"] == "loopback":
344 interface_name = destRouterLink
345 else:
346 interface_name = data["interface"]
347
348 cmd = "interface {}".format(interface_name)
349 config_data.append(cmd)
350 config_data.append("ip pim")
351
352 # pim global config
353 if "pim" in input_dict[router]:
354 pim_data = input_dict[router]["pim"]
355 del_action = pim_data.setdefault("delete", False)
356 for t in [
a53c08bc
CH
357 "join-prune-interval",
358 "keep-alive-timer",
359 "register-suppress-time",
4f99894d
CH
360 ]:
361 if t in pim_data:
362 cmd = "ip pim {} {}".format(t, pim_data[t])
363 if del_action:
364 cmd = "no {}".format(cmd)
0a76e764 365 config_data.append(cmd)
e8cd26fd 366
4f99894d 367 return config_data
e8cd26fd 368
369
e8cd26fd 370def find_rp_details(tgen, topo):
371 """
372 Find who is RP in topology and returns list of RPs
373
374 Parameters:
375 -----------
376 * `tgen` : Topogen object
377 * `topo` : json file data
378
379 returns:
380 --------
381 errormsg or True
382 """
383
384 rp_details = {}
385
386 router_list = tgen.routers()
387 topo_data = topo["routers"]
388
389 for router in router_list.keys():
390
391 if "pim" not in topo_data[router]:
392 continue
393
394 pim_data = topo_data[router]["pim"]
395 if "rp" in pim_data:
396 rp_data = pim_data["rp"]
397 for rp_dict in rp_data:
398 # ip address of RP
399 rp_addr = rp_dict["rp_addr"]
400
401 for link, data in topo["routers"][router]["links"].items():
402 if data["ipv4"].split("/")[0] == rp_addr:
403 rp_details[router] = rp_addr
404
405 return rp_details
406
407
408def configure_pim_force_expire(tgen, topo, input_dict, build=False):
409 """
410 Helper API to create pim configuration.
411
412 Parameters
413 ----------
414 * `tgen` : Topogen object
415 * `topo` : json file data
416 * `input_dict` : Input dict data, required when configuring from testcase
417 * `build` : Only for initial setup phase this is set as True.
418
419 Usage
420 -----
421 input_dict ={
422 "l1": {
423 "pim": {
424 "force_expire":{
425 "10.0.10.1": ["255.1.1.1"]
426 }
427 }
428 }
429 }
430
431 result = create_pim_config(tgen, topo, input_dict)
432
433 Returns
434 -------
435 True or False
436 """
437
438 result = False
439 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
4f99894d 440
e8cd26fd 441 try:
4f99894d 442 config_data_dict = {}
e8cd26fd 443
444 for dut in input_dict.keys():
445 if "pim" not in input_dict[dut]:
446 continue
447
448 pim_data = input_dict[dut]["pim"]
449
4f99894d 450 config_data = []
e8cd26fd 451 if "force_expire" in pim_data:
e8cd26fd 452 force_expire_data = pim_data["force_expire"]
453
454 for source, groups in force_expire_data.items():
455 if type(groups) is not list:
456 groups = [groups]
457
458 for group in groups:
459 cmd = "ip pim force-expire source {} group {}".format(
460 source, group
461 )
462 config_data.append(cmd)
463
4f99894d
CH
464 if config_data:
465 config_data_dict[dut] = config_data
e8cd26fd 466
4f99894d
CH
467 result = create_common_configurations(
468 tgen, config_data_dict, "pim", build=build
469 )
e8cd26fd 470 except InvalidCLIError:
4f99894d
CH
471 logger.error("configure_pim_force_expire", exc_info=True)
472 result = False
e8cd26fd 473
474 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
475 return result
476
477
478#############################################
479# Verification APIs
480#############################################
ed776e38 481@retry(retry_timeout=12)
3c41ebf8 482def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None, expected=True):
e8cd26fd 483 """
484 Verify all PIM neighbors are up and running, config is verified
485 using "show ip pim neighbor" cli
486
487 Parameters
488 ----------
489 * `tgen`: topogen object
490 * `topo` : json file data
491 * `dut` : dut info
492 * `iface` : link for which PIM nbr need to check
eab72dc8 493 * `nbr_ip` : neighbor ip of interface
3c41ebf8 494 * `expected` : expected results from API, by-default True
e8cd26fd 495
496 Usage
497 -----
eab72dc8 498 result = verify_pim_neighbors(tgen, topo, dut, iface=ens192, nbr_ip=20.1.1.2)
e8cd26fd 499
500 Returns
501 -------
502 errormsg(str) or True
503 """
504
505 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
506
507 for router in tgen.routers():
508 if dut is not None and dut != router:
509 continue
510
511 rnode = tgen.routers()[router]
512 show_ip_pim_neighbor_json = rnode.vtysh_cmd(
513 "show ip pim neighbor json", isjson=True
514 )
515
516 for destLink, data in topo["routers"][router]["links"].items():
517 if iface is not None and iface != data["interface"]:
518 continue
519
520 if "type" in data and data["type"] == "loopback":
521 continue
522
523 if "pim" not in data:
524 continue
525
526 if "pim" in data and data["pim"] == "enable":
527 local_interface = data["interface"]
528
529 if "-" in destLink:
530 # Spliting and storing destRouterLink data in tempList
531 tempList = destLink.split("-")
532
533 # destRouter
534 destLink = tempList.pop(0)
535
536 # Current Router Link
537 tempList.insert(0, router)
538 curRouter = "-".join(tempList)
539 else:
540 curRouter = router
541 if destLink not in topo["routers"]:
542 continue
543 data = topo["routers"][destLink]["links"][curRouter]
544 if "type" in data and data["type"] == "loopback":
545 continue
546
547 if "pim" not in data:
548 continue
549
550 logger.info("[DUT: %s]: Verifying PIM neighbor status:", router)
551
552 if "pim" in data and data["pim"] == "enable":
553 pim_nh_intf_ip = data["ipv4"].split("/")[0]
554
555 # Verifying PIM neighbor
556 if local_interface in show_ip_pim_neighbor_json:
557 if show_ip_pim_neighbor_json[local_interface]:
558 if (
559 show_ip_pim_neighbor_json[local_interface][pim_nh_intf_ip][
560 "neighbor"
561 ]
562 != pim_nh_intf_ip
563 ):
564 errormsg = (
565 "[DUT %s]: Local interface: %s, PIM"
566 " neighbor check failed "
567 "Expected neighbor: %s, Found neighbor:"
568 " %s"
569 % (
570 router,
571 local_interface,
572 pim_nh_intf_ip,
573 show_ip_pim_neighbor_json[local_interface][
574 pim_nh_intf_ip
575 ]["neighbor"],
576 )
577 )
578 return errormsg
579
580 logger.info(
581 "[DUT %s]: Local interface: %s, Found"
582 " expected PIM neighbor %s",
583 router,
584 local_interface,
585 pim_nh_intf_ip,
586 )
587 else:
588 errormsg = (
589 "[DUT %s]: Local interface: %s, and"
590 "interface ip: %s is not found in "
591 "PIM neighbor " % (router, local_interface, pim_nh_intf_ip)
592 )
593 return errormsg
594 else:
595 errormsg = (
596 "[DUT %s]: Local interface: %s, is not "
597 "present in PIM neighbor " % (router, local_interface)
598 )
599 return errormsg
600
601 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
602 return True
603
604
ed776e38 605@retry(retry_timeout=40)
3c41ebf8 606def verify_igmp_groups(tgen, dut, interface, group_addresses, expected=True):
e8cd26fd 607 """
608 Verify IGMP groups are received from an intended interface
609 by running "show ip igmp groups" command
610
611 Parameters
612 ----------
613 * `tgen`: topogen object
614 * `dut`: device under test
615 * `interface`: interface, from which IGMP groups would be received
616 * `group_addresses`: IGMP group address
3c41ebf8 617 * `expected` : expected results from API, by-default True
e8cd26fd 618
619 Usage
620 -----
621 dut = "r1"
622 interface = "r1-r0-eth0"
623 group_address = "225.1.1.1"
624 result = verify_igmp_groups(tgen, dut, interface, group_address)
625
626 Returns
627 -------
628 errormsg(str) or True
629 """
630
631 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
632
633 if dut not in tgen.routers():
634 return False
635
636 rnode = tgen.routers()[dut]
637
638 logger.info("[DUT: %s]: Verifying IGMP groups received:", dut)
639 show_ip_igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True)
640
641 if type(group_addresses) is not list:
642 group_addresses = [group_addresses]
643
644 if interface in show_ip_igmp_json:
645 show_ip_igmp_json = show_ip_igmp_json[interface]["groups"]
646 else:
647 errormsg = (
648 "[DUT %s]: Verifying IGMP group received"
649 " from interface %s [FAILED]!! " % (dut, interface)
650 )
651 return errormsg
652
653 found = False
654 for grp_addr in group_addresses:
655 for index in show_ip_igmp_json:
656 if index["group"] == grp_addr:
657 found = True
658 break
659 if found is not True:
660 errormsg = (
661 "[DUT %s]: Verifying IGMP group received"
662 " from interface %s [FAILED]!! "
663 " Expected not found: %s" % (dut, interface, grp_addr)
664 )
665 return errormsg
666
667 logger.info(
668 "[DUT %s]: Verifying IGMP group %s received "
669 "from interface %s [PASSED]!! ",
670 dut,
671 grp_addr,
672 interface,
673 )
674
675 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
676 return True
677
678
ed776e38 679@retry(retry_timeout=60)
e8cd26fd 680def verify_upstream_iif(
a53c08bc
CH
681 tgen,
682 dut,
683 iif,
684 src_address,
685 group_addresses,
686 joinState=None,
687 refCount=1,
688 expected=True,
e8cd26fd 689):
690 """
691 Verify upstream inbound interface is updated correctly
692 by running "show ip pim upstream" cli
693
694 Parameters
695 ----------
696 * `tgen`: topogen object
697 * `dut`: device under test
698 * `iif`: inbound interface
699 * `src_address`: source address
700 * `group_addresses`: IGMP group address
701 * `joinState`: upstream join state
702 * `refCount`: refCount value
3c41ebf8 703 * `expected` : expected results from API, by-default True
e8cd26fd 704
705 Usage
706 -----
707 dut = "r1"
708 iif = "r1-r0-eth0"
709 src_address = "*"
710 group_address = "225.1.1.1"
711 result = verify_upstream_iif(tgen, dut, iif, src_address, group_address,
712 state, refCount)
713
714 Returns
715 -------
716 errormsg(str) or True
717 """
718
719 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
720
721 if dut not in tgen.routers():
722 return False
723
724 rnode = tgen.routers()[dut]
725
726 logger.info(
727 "[DUT: %s]: Verifying upstream Inbound Interface" " for IGMP groups received:",
728 dut,
729 )
730 show_ip_pim_upstream_json = run_frr_cmd(
731 rnode, "show ip pim upstream json", isjson=True
732 )
733
734 if type(group_addresses) is not list:
735 group_addresses = [group_addresses]
736
737 if type(iif) is not list:
738 iif = [iif]
739
740 for grp_addr in group_addresses:
741 # Verify group address
742 if grp_addr not in show_ip_pim_upstream_json:
743 errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % (
744 dut,
745 grp_addr,
746 )
747 return errormsg
748 group_addr_json = show_ip_pim_upstream_json[grp_addr]
749
750 # Verify source address
751 if src_address not in group_addr_json:
752 errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % (
753 dut,
754 src_address,
755 grp_addr,
756 )
757 return errormsg
758
759 # Verify Inbound Interface
760 found = False
761 for in_interface in iif:
762 if group_addr_json[src_address]["inboundInterface"] == in_interface:
763 if refCount > 0:
764 logger.info(
765 "[DUT %s]: Verifying refCount "
766 "for (%s,%s) [PASSED]!! "
767 " Found Expected: %s",
768 dut,
769 src_address,
770 grp_addr,
771 group_addr_json[src_address]["refCount"],
772 )
773 found = True
774 if found:
775 if joinState is None:
776 if group_addr_json[src_address]["joinState"] != "Joined":
777 errormsg = (
778 "[DUT %s]: Verifying iif "
779 "(Inbound Interface) for (%s,%s) and"
780 " joinState :%s [FAILED]!! "
781 " Expected: %s, Found: %s"
782 % (
783 dut,
784 src_address,
785 grp_addr,
786 group_addr_json[src_address]["joinState"],
787 in_interface,
788 group_addr_json[src_address]["inboundInterface"],
789 )
790 )
791 return errormsg
792
793 elif group_addr_json[src_address]["joinState"] != joinState:
794 errormsg = (
795 "[DUT %s]: Verifying iif "
796 "(Inbound Interface) for (%s,%s) and"
797 " joinState :%s [FAILED]!! "
798 " Expected: %s, Found: %s"
799 % (
800 dut,
801 src_address,
802 grp_addr,
803 group_addr_json[src_address]["joinState"],
804 in_interface,
805 group_addr_json[src_address]["inboundInterface"],
806 )
807 )
808 return errormsg
809
810 logger.info(
811 "[DUT %s]: Verifying iif(Inbound Interface)"
812 " for (%s,%s) and joinState is %s [PASSED]!! "
813 " Found Expected: (%s)",
814 dut,
815 src_address,
816 grp_addr,
817 group_addr_json[src_address]["joinState"],
818 group_addr_json[src_address]["inboundInterface"],
819 )
820 if not found:
821 errormsg = (
822 "[DUT %s]: Verifying iif "
823 "(Inbound Interface) for (%s, %s) "
824 "[FAILED]!! "
825 " Expected: %s, Found: %s"
826 % (
827 dut,
828 src_address,
829 grp_addr,
830 in_interface,
831 group_addr_json[src_address]["inboundInterface"],
832 )
833 )
834 return errormsg
835
836 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
837 return True
838
839
ed776e38 840@retry(retry_timeout=12)
a53c08bc
CH
841def verify_join_state_and_timer(
842 tgen, dut, iif, src_address, group_addresses, expected=True
843):
e8cd26fd 844 """
845 Verify join state is updated correctly and join timer is
846 running with the help of "show ip pim upstream" cli
847
848 Parameters
849 ----------
850 * `tgen`: topogen object
851 * `dut`: device under test
852 * `iif`: inbound interface
853 * `src_address`: source address
854 * `group_addresses`: IGMP group address
3c41ebf8 855 * `expected` : expected results from API, by-default True
e8cd26fd 856
857 Usage
858 -----
859 dut = "r1"
860 iif = "r1-r0-eth0"
861 group_address = "225.1.1.1"
862 result = verify_join_state_and_timer(tgen, dut, iif, group_address)
863
864 Returns
865 -------
866 errormsg(str) or True
867 """
868
869 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
870 errormsg = ""
871
872 if dut not in tgen.routers():
873 return False
874
875 rnode = tgen.routers()[dut]
876
877 logger.info(
878 "[DUT: %s]: Verifying Join state and Join Timer" " for IGMP groups received:",
879 dut,
880 )
881 show_ip_pim_upstream_json = run_frr_cmd(
882 rnode, "show ip pim upstream json", isjson=True
883 )
884
885 if type(group_addresses) is not list:
886 group_addresses = [group_addresses]
887
888 for grp_addr in group_addresses:
889 # Verify group address
890 if grp_addr not in show_ip_pim_upstream_json:
891 errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % (
892 dut,
893 grp_addr,
894 )
895 return errormsg
896
897 group_addr_json = show_ip_pim_upstream_json[grp_addr]
898
899 # Verify source address
900 if src_address not in group_addr_json:
901 errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % (
902 dut,
903 src_address,
904 grp_addr,
905 )
906 return errormsg
907
908 # Verify join state
909 joinState = group_addr_json[src_address]["joinState"]
910 if joinState != "Joined":
911 error = (
912 "[DUT %s]: Verifying join state for"
913 " (%s,%s) [FAILED]!! "
914 " Expected: %s, Found: %s"
915 % (dut, src_address, grp_addr, "Joined", joinState)
916 )
917 errormsg = errormsg + "\n" + str(error)
918 else:
919 logger.info(
920 "[DUT %s]: Verifying join state for"
921 " (%s,%s) [PASSED]!! "
922 " Found Expected: %s",
923 dut,
924 src_address,
925 grp_addr,
926 joinState,
927 )
928
929 # Verify join timer
930 joinTimer = group_addr_json[src_address]["joinTimer"]
931 if not re.match(r"(\d{2}):(\d{2}):(\d{2})", joinTimer):
932 error = (
933 "[DUT %s]: Verifying join timer for"
934 " (%s,%s) [FAILED]!! "
49581587
CH
935 " Expected: %s, Found: %s"
936 ) % (
e8cd26fd 937 dut,
938 src_address,
939 grp_addr,
940 "join timer should be running",
941 joinTimer,
942 )
943 errormsg = errormsg + "\n" + str(error)
944 else:
945 logger.info(
946 "[DUT %s]: Verifying join timer is running"
947 " for (%s,%s) [PASSED]!! "
948 " Found Expected: %s",
949 dut,
950 src_address,
951 grp_addr,
952 joinTimer,
953 )
954
955 if errormsg != "":
956 return errormsg
957
958 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
959 return True
960
961
4f99894d 962@retry(retry_timeout=120)
e8cd26fd 963def verify_ip_mroutes(
a53c08bc
CH
964 tgen,
965 dut,
966 src_address,
967 group_addresses,
968 iif,
969 oil,
970 return_uptime=False,
971 mwait=0,
972 expected=True,
e8cd26fd 973):
974 """
975 Verify ip mroutes and make sure (*, G)/(S, G) is present in mroutes
976 by running "show ip pim upstream" cli
977
978 Parameters
979 ----------
980 * `tgen`: topogen object
981 * `dut`: device under test
982 * `src_address`: source address
983 * `group_addresses`: IGMP group address
984 * `iif`: Incoming interface
985 * `oil`: Outgoing interface
986 * `return_uptime`: If True, return uptime dict, default is False
987 * `mwait`: Wait time, default is 0
3c41ebf8 988 * `expected` : expected results from API, by-default True
e8cd26fd 989
990 Usage
991 -----
992 dut = "r1"
993 group_address = "225.1.1.1"
994 result = verify_ip_mroutes(tgen, dut, src_address, group_address)
995
996 Returns
997 -------
998 errormsg(str) or True
999 """
1000
1001 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
1002
1003 if dut not in tgen.routers():
1004 return False
1005
1006 rnode = tgen.routers()[dut]
1007
1008 if return_uptime:
1009 logger.info("Sleeping for %s sec..", mwait)
1010 sleep(mwait)
1011
1012 logger.info("[DUT: %s]: Verifying ip mroutes", dut)
1013 show_ip_mroute_json = run_frr_cmd(rnode, "show ip mroute json", isjson=True)
1014
1015 if return_uptime:
1016 uptime_dict = {}
1017
1018 if bool(show_ip_mroute_json) == False:
1019 error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut)
1020 return error_msg
1021
1022 if not isinstance(group_addresses, list):
1023 group_addresses = [group_addresses]
1024
94e8feaa 1025 if not isinstance(iif, list) and iif != "none":
e8cd26fd 1026 iif = [iif]
1027
94e8feaa 1028 if not isinstance(oil, list) and oil != "none":
e8cd26fd 1029 oil = [oil]
1030
1031 for grp_addr in group_addresses:
1032 if grp_addr not in show_ip_mroute_json:
1033 errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
1034 dut,
1035 src_address,
1036 grp_addr,
1037 )
1038 return errormsg
1039 else:
1040 if return_uptime:
1041 uptime_dict[grp_addr] = {}
1042
1043 group_addr_json = show_ip_mroute_json[grp_addr]
1044
1045 if src_address not in group_addr_json:
1046 errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
1047 dut,
1048 src_address,
1049 grp_addr,
1050 )
1051 return errormsg
1052 else:
1053 if return_uptime:
1054 uptime_dict[grp_addr][src_address] = {}
1055
1056 mroutes = group_addr_json[src_address]
1057
1058 if mroutes["installed"] != 0:
1059 logger.info(
1060 "[DUT %s]: mroute (%s,%s) is installed", dut, src_address, grp_addr
1061 )
1062
1063 if "oil" not in mroutes:
1064 if oil == "none" and mroutes["iif"] in iif:
1065 logger.info(
1066 "[DUT %s]: Verifying (%s, %s) mroute,"
1067 " [PASSED]!! Found Expected: "
1068 "(iif: %s, oil: %s, installed: (%s,%s))",
1069 dut,
1070 src_address,
1071 grp_addr,
1072 mroutes["iif"],
1073 oil,
1074 src_address,
1075 grp_addr,
1076 )
1077 else:
1078 errormsg = (
1079 "[DUT %s]: Verifying (%s, %s) mroute,"
1080 " [FAILED]!! "
1081 "Expected: (oil: %s, installed:"
1082 " (%s,%s)) Found: ( oil: none, "
1083 "installed: (%s,%s))"
1084 % (
1085 dut,
1086 src_address,
1087 grp_addr,
1088 oil,
1089 src_address,
1090 grp_addr,
1091 src_address,
1092 grp_addr,
1093 )
1094 )
1095
1096 return errormsg
1097
1098 else:
1099 found = False
1100 for route, data in mroutes["oil"].items():
1101 if route in oil and route != "pimreg":
1102 if (
1103 data["source"] == src_address
1104 and data["group"] == grp_addr
1105 and data["inboundInterface"] in iif
1106 and data["outboundInterface"] in oil
1107 ):
1108 if return_uptime:
1109
1110 uptime_dict[grp_addr][src_address] = data["upTime"]
1111
1112 logger.info(
1113 "[DUT %s]: Verifying (%s, %s)"
1114 " mroute, [PASSED]!! "
1115 "Found Expected: "
1116 "(iif: %s, oil: %s, installed:"
1117 " (%s,%s)",
1118 dut,
1119 src_address,
1120 grp_addr,
1121 data["inboundInterface"],
1122 data["outboundInterface"],
1123 data["source"],
1124 data["group"],
1125 )
1126 found = True
1127 break
1128 else:
1129 continue
1130
1131 if not found:
1132 errormsg = (
1133 "[DUT %s]: Verifying (%s, %s)"
1134 " mroute [FAILED]!! "
1135 "Expected in: (iif: %s, oil: %s,"
1136 " installed: (%s,%s)) Found: "
1137 "(iif: %s, oil: %s, "
1138 "installed: (%s,%s))"
1139 % (
1140 dut,
1141 src_address,
1142 grp_addr,
1143 iif,
1144 oil,
1145 src_address,
1146 grp_addr,
1147 data["inboundInterface"],
1148 data["outboundInterface"],
1149 data["source"],
1150 data["group"],
1151 )
1152 )
1153 return errormsg
1154
1155 else:
1156 errormsg = "[DUT %s]: mroute (%s,%s) is not installed" % (
1157 dut,
1158 src_address,
1159 grp_addr,
1160 )
1161 return errormsg
1162
1163 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1164 return True if return_uptime == False else uptime_dict
1165
1166
ed776e38 1167@retry(retry_timeout=60)
e8cd26fd 1168def verify_pim_rp_info(
a53c08bc
CH
1169 tgen,
1170 topo,
1171 dut,
1172 group_addresses,
1173 oif=None,
1174 rp=None,
1175 source=None,
1176 iamrp=None,
1177 expected=True,
e8cd26fd 1178):
1179 """
1180 Verify pim rp info by running "show ip pim rp-info" cli
1181
1182 Parameters
1183 ----------
1184 * `tgen`: topogen object
1185 * `topo`: JSON file handler
1186 * `dut`: device under test
1187 * `group_addresses`: IGMP group address
1188 * `oif`: outbound interface name
1189 * `rp`: RP address
1190 * `source`: Source of RP
1191 * `iamrp`: User defined RP
3c41ebf8 1192 * `expected` : expected results from API, by-default True
e8cd26fd 1193
1194 Usage
1195 -----
1196 dut = "r1"
1197 result = verify_pim_rp_info(tgen, topo, dut, group_address,
1198 rp=rp, source="BSR")
1199
1200 Returns
1201 -------
1202 errormsg(str) or True
1203 """
1204
1205 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
1206
1207 if dut not in tgen.routers():
1208 return False
1209
1210 rnode = tgen.routers()[dut]
1211
1212 logger.info("[DUT: %s]: Verifying ip rp info", dut)
1213 show_ip_rp_info_json = run_frr_cmd(rnode, "show ip pim rp-info json", isjson=True)
1214
1215 if type(group_addresses) is not list:
1216 group_addresses = [group_addresses]
1217
1218 if type(oif) is not list:
1219 oif = [oif]
1220
1221 for grp_addr in group_addresses:
1222 if rp is None:
1223 rp_details = find_rp_details(tgen, topo)
1224
1225 if dut in rp_details:
1226 iamRP = True
1227 else:
1228 iamRP = False
1229 else:
1230 show_ip_route_json = run_frr_cmd(
1231 rnode, "show ip route connected json", isjson=True
1232 )
1233 for _rp in show_ip_route_json.keys():
1234 if rp == _rp.split("/")[0]:
1235 iamRP = True
1236 break
1237 else:
1238 iamRP = False
1239
1240 if rp not in show_ip_rp_info_json:
1241 errormsg = "[DUT %s]: Verifying rp-info" "for rp_address %s [FAILED]!! " % (
1242 dut,
1243 rp,
1244 )
1245 return errormsg
1246 else:
1247 group_addr_json = show_ip_rp_info_json[rp]
1248
1249 for rp_json in group_addr_json:
1250 if oif is not None:
1251 found = False
1252 if rp_json["outboundInterface"] not in oif:
1253 errormsg = (
1254 "[DUT %s]: Verifying OIF "
1255 "for group %s and RP %s [FAILED]!! "
1256 "Expected interfaces: (%s),"
1257 " Found: (%s)"
1258 % (dut, grp_addr, rp, oif, rp_json["outboundInterface"])
1259 )
1260 return errormsg
1261
1262 logger.info(
1263 "[DUT %s]: Verifying OIF "
1264 "for group %s and RP %s [PASSED]!! "
1265 "Found Expected: (%s)"
1266 % (dut, grp_addr, rp, rp_json["outboundInterface"])
1267 )
1268
1269 if source is not None:
1270 if rp_json["source"] != source:
1271 errormsg = (
1272 "[DUT %s]: Verifying SOURCE "
1273 "for group %s and RP %s [FAILED]!! "
1274 "Expected: (%s),"
1275 " Found: (%s)" % (dut, grp_addr, rp, source, rp_json["source"])
1276 )
1277 return errormsg
1278
1279 logger.info(
1280 "[DUT %s]: Verifying SOURCE "
1281 "for group %s and RP %s [PASSED]!! "
1282 "Found Expected: (%s)" % (dut, grp_addr, rp, rp_json["source"])
1283 )
1284
1285 if rp_json["group"] == grp_addr and iamrp is not None:
1286 if iamRP:
1287 if rp_json["iAmRP"]:
1288 logger.info(
1289 "[DUT %s]: Verifying group "
1290 "and iAmRP [PASSED]!!"
1291 " Found Expected: (%s, %s:%s)",
1292 dut,
1293 grp_addr,
1294 "iAmRP",
1295 rp_json["iAmRP"],
1296 )
1297 else:
1298 errormsg = (
1299 "[DUT %s]: Verifying group"
1300 "%s and iAmRP [FAILED]!! "
1301 "Expected: (iAmRP: %s),"
1302 " Found: (iAmRP: %s)"
1303 % (dut, grp_addr, "true", rp_json["iAmRP"])
1304 )
1305 return errormsg
1306
1307 if not iamRP:
1308 if rp_json["iAmRP"] == False:
1309 logger.info(
1310 "[DUT %s]: Verifying group "
1311 "and iAmNotRP [PASSED]!!"
1312 " Found Expected: (%s, %s:%s)",
1313 dut,
1314 grp_addr,
1315 "iAmRP",
1316 rp_json["iAmRP"],
1317 )
1318 else:
1319 errormsg = (
1320 "[DUT %s]: Verifying group"
1321 "%s and iAmRP [FAILED]!! "
1322 "Expected: (iAmRP: %s),"
1323 " Found: (iAmRP: %s)"
1324 % (dut, grp_addr, "false", rp_json["iAmRP"])
1325 )
1326 return errormsg
1327
1328 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1329 return True
1330
1331
ed776e38 1332@retry(retry_timeout=60)
e8cd26fd 1333def verify_pim_state(
a53c08bc
CH
1334 tgen,
1335 dut,
1336 iif,
1337 oil,
1338 group_addresses,
1339 src_address=None,
1340 installed_fl=None,
1341 expected=True,
e8cd26fd 1342):
1343 """
1344 Verify pim state by running "show ip pim state" cli
1345
1346 Parameters
1347 ----------
1348 * `tgen`: topogen object
1349 * `dut`: device under test
1350 * `iif`: inbound interface
1351 * `oil`: outbound interface
1352 * `group_addresses`: IGMP group address
1353 * `src_address`: source address, default = None
1354 * installed_fl` : Installed flag
3c41ebf8 1355 * `expected` : expected results from API, by-default True
e8cd26fd 1356
1357 Usage
1358 -----
1359 dut = "r1"
1360 iif = "r1-r3-eth1"
1361 oil = "r1-r0-eth0"
1362 group_address = "225.1.1.1"
1363 result = verify_pim_state(tgen, dut, iif, oil, group_address)
1364
1365 Returns
1366 -------
1367 errormsg(str) or True
1368 """
1369
1370 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
1371
1372 if dut not in tgen.routers():
1373 return False
1374
1375 rnode = tgen.routers()[dut]
1376
1377 logger.info("[DUT: %s]: Verifying pim state", dut)
1378 show_pim_state_json = run_frr_cmd(rnode, "show ip pim state json", isjson=True)
1379
1380 if installed_fl is None:
1381 installed_fl = 1
1382
1383 if type(group_addresses) is not list:
1384 group_addresses = [group_addresses]
1385
1386 for grp_addr in group_addresses:
1387 if src_address is None:
1388 src_address = "*"
1389 pim_state_json = show_pim_state_json[grp_addr][src_address]
1390 else:
1391 pim_state_json = show_pim_state_json[grp_addr][src_address]
1392
1393 if pim_state_json["Installed"] == installed_fl:
1394 logger.info(
1395 "[DUT %s]: group %s is installed flag: %s",
1396 dut,
1397 grp_addr,
1398 pim_state_json["Installed"],
1399 )
1400 for interface, data in pim_state_json[iif].items():
1401 if interface != oil:
1402 continue
1403
1404 # Verify iif, oil and installed state
1405 if (
1406 data["group"] == grp_addr
1407 and data["installed"] == installed_fl
1408 and data["inboundInterface"] == iif
1409 and data["outboundInterface"] == oil
1410 ):
1411 logger.info(
1412 "[DUT %s]: Verifying pim state for group"
1413 " %s [PASSED]!! Found Expected: "
1414 "(iif: %s, oil: %s, installed: %s) ",
1415 dut,
1416 grp_addr,
1417 data["inboundInterface"],
1418 data["outboundInterface"],
1419 data["installed"],
1420 )
1421 else:
1422 errormsg = (
1423 "[DUT %s]: Verifying pim state for group"
1424 " %s, [FAILED]!! Expected: "
5aab262b
QY
1425 "(iif: %s, oil: %s, installed: %s) "
1426 % (dut, grp_addr, iif, oil, "1"),
e8cd26fd 1427 "Found: (iif: %s, oil: %s, installed: %s)"
1428 % (
e8cd26fd 1429 data["inboundInterface"],
1430 data["outboundInterface"],
1431 data["installed"],
1432 ),
1433 )
1434 return errormsg
1435 else:
1436 errormsg = "[DUT %s]: %s install flag value not as expected" % (
1437 dut,
1438 grp_addr,
1439 )
1440 return errormsg
1441
1442 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1443 return True
1444
1445
1446def verify_pim_interface_traffic(tgen, input_dict):
1447 """
1448 Verify ip pim interface traffice by running
1449 "show ip pim interface traffic" cli
1450
1451 Parameters
1452 ----------
1453 * `tgen`: topogen object
1454 * `input_dict(dict)`: defines DUT, what and from which interfaces
1455 traffic needs to be verified
1456 Usage
1457 -----
1458 input_dict = {
1459 "r1": {
1460 "r1-r0-eth0": {
1461 "helloRx": 0,
1462 "helloTx": 1,
1463 "joinRx": 0,
1464 "joinTx": 0
1465 }
1466 }
1467 }
1468
1469 result = verify_pim_interface_traffic(tgen, input_dict)
1470
1471 Returns
1472 -------
1473 errormsg(str) or True
1474 """
1475
1476 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
1477
1478 output_dict = {}
1479 for dut in input_dict.keys():
1480 if dut not in tgen.routers():
1481 continue
1482
1483 rnode = tgen.routers()[dut]
1484
1485 logger.info("[DUT: %s]: Verifying pim interface traffic", dut)
e8cd26fd 1486
3d7d6e9a
DS
1487 def show_pim_intf_traffic(rnode, dut, input_dict, output_dict):
1488 show_pim_intf_traffic_json = run_frr_cmd(
1489 rnode, "show ip pim interface traffic json", isjson=True
1490 )
e8cd26fd 1491
3d7d6e9a
DS
1492 output_dict[dut] = {}
1493 for intf, data in input_dict[dut].items():
1494 interface_json = show_pim_intf_traffic_json[intf]
1495 for state in data:
1496
1497 # Verify Tx/Rx
1498 if state in interface_json:
1499 output_dict[dut][state] = interface_json[state]
1500 else:
1501 errormsg = (
1502 "[DUT %s]: %s is not present"
1503 "for interface %s [FAILED]!! " % (dut, state, intf)
1504 )
1505 return errormsg
1506 return None
1507
1508 test_func = functools.partial(
1509 show_pim_intf_traffic, rnode, dut, input_dict, output_dict
1510 )
1511 (result, out) = topotest.run_and_expect(test_func, None, count=20, wait=1)
1512 if not result:
1513 return out
e8cd26fd 1514
1515 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1516 return output_dict
1517
1518
ed776e38 1519@retry(retry_timeout=40)
a53c08bc
CH
1520def verify_pim_interface(
1521 tgen, topo, dut, interface=None, interface_ip=None, expected=True
1522):
e8cd26fd 1523 """
1524 Verify all PIM interface are up and running, config is verified
1525 using "show ip pim interface" cli
1526
1527 Parameters
1528 ----------
1529 * `tgen`: topogen object
1530 * `topo` : json file data
1531 * `dut` : device under test
eab72dc8 1532 * `interface` : interface name
1533 * `interface_ip` : interface ip address
3c41ebf8 1534 * `expected` : expected results from API, by-default True
e8cd26fd 1535
1536 Usage
1537 -----
eab72dc8 1538 result = verify_pim_interfacetgen, topo, dut, interface=ens192, interface_ip=20.1.1.1)
e8cd26fd 1539
1540 Returns
1541 -------
1542 errormsg(str) or True
1543 """
1544
1545 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
1546
1547 for router in tgen.routers():
1548 if router != dut:
1549 continue
1550
1551 logger.info("[DUT: %s]: Verifying PIM interface status:", dut)
1552
1553 rnode = tgen.routers()[dut]
5980ad0a
DS
1554 show_ip_pim_interface_json = rnode.vtysh_cmd(
1555 "show ip pim interface json", isjson=True
1556 )
eab72dc8 1557
5980ad0a 1558 logger.info("show_ip_pim_interface_json: \n %s", show_ip_pim_interface_json)
eab72dc8 1559
1560 if interface_ip:
1561 if interface in show_ip_pim_interface_json:
1562 pim_intf_json = show_ip_pim_interface_json[interface]
1563 if pim_intf_json["address"] != interface_ip:
5980ad0a
DS
1564 errormsg = (
1565 "[DUT %s]: PIM interface "
1566 "ip is not correct "
1567 "[FAILED]!! Expected : %s, Found : %s"
1568 % (dut, pim_intf_json["address"], interface_ip)
1569 )
eab72dc8 1570 return errormsg
1571 else:
5980ad0a
DS
1572 logger.info(
1573 "[DUT %s]: PIM interface "
1574 "ip is correct "
1575 "[Passed]!! Expected : %s, Found : %s"
1576 % (dut, pim_intf_json["address"], interface_ip)
1577 )
eab72dc8 1578 return True
1579 else:
1580 for destLink, data in topo["routers"][dut]["links"].items():
1581 if "type" in data and data["type"] == "loopback":
1582 continue
e8cd26fd 1583
eab72dc8 1584 if "pim" in data and data["pim"] == "enable":
1585 pim_interface = data["interface"]
1586 pim_intf_ip = data["ipv4"].split("/")[0]
e8cd26fd 1587
eab72dc8 1588 if pim_interface in show_ip_pim_interface_json:
5980ad0a 1589 pim_intf_json = show_ip_pim_interface_json[pim_interface]
e8cd26fd 1590
1591 # Verifying PIM interface
5980ad0a
DS
1592 if (
1593 pim_intf_json["address"] != pim_intf_ip
1594 and pim_intf_json["state"] != "up"
1595 ):
1596 errormsg = (
1597 "[DUT %s]: PIM interface: %s "
1598 "PIM interface ip: %s, status check "
1599 "[FAILED]!! Expected : %s, Found : %s"
1600 % (
1601 dut,
1602 pim_interface,
1603 pim_intf_ip,
1604 pim_interface,
1605 pim_intf_json["state"],
1606 )
1607 )
e8cd26fd 1608 return errormsg
1609
5980ad0a
DS
1610 logger.info(
1611 "[DUT %s]: PIM interface: %s, "
1612 "interface ip: %s, status: %s"
1613 " [PASSED]!!",
1614 dut,
1615 pim_interface,
1616 pim_intf_ip,
1617 pim_intf_json["state"],
1618 )
e8cd26fd 1619
1620 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1621 return True
1622
1623
1624def clear_ip_pim_interface_traffic(tgen, topo):
1625 """
1626 Clear ip pim interface traffice by running
1627 "clear ip pim interface traffic" cli
1628
1629 Parameters
1630 ----------
1631 * `tgen`: topogen object
1632 Usage
1633 -----
1634
1635 result = clear_ip_pim_interface_traffic(tgen, topo)
1636
1637 Returns
1638 -------
1639 errormsg(str) or True
1640 """
1641
1642 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
1643
1644 for dut in tgen.routers():
1645 if "pim" not in topo["routers"][dut]:
1646 continue
1647
1648 rnode = tgen.routers()[dut]
1649
1650 logger.info("[DUT: %s]: Clearing pim interface traffic", dut)
1651 result = run_frr_cmd(rnode, "clear ip pim interface traffic")
1652
1653 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1654
1655 return True
1656
1657
1658def clear_ip_pim_interfaces(tgen, dut):
1659 """
1660 Clear ip pim interface by running
1661 "clear ip pim interfaces" cli
1662
1663 Parameters
1664 ----------
1665 * `tgen`: topogen object
1666 * `dut`: Device Under Test
1667 Usage
1668 -----
1669
1670 result = clear_ip_pim_interfaces(tgen, dut)
1671
1672 Returns
1673 -------
1674 errormsg(str) or True
1675 """
1676
1677 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
1678
1679 nh_before_clear = {}
1680 nh_after_clear = {}
1681
1682 rnode = tgen.routers()[dut]
1683
1684 logger.info("[DUT: %s]: Verify pim neighbor before pim" " neighbor clear", dut)
1685 # To add uptime initially
1686 sleep(10)
1687 run_json_before = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True)
1688
1689 for key, value in run_json_before.items():
1690 if bool(value):
1691 for _key, _value in value.items():
1692 nh_before_clear[key] = _value["upTime"]
1693
1694 # Clearing PIM neighbors
1695 logger.info("[DUT: %s]: Clearing pim interfaces", dut)
1696 run_frr_cmd(rnode, "clear ip pim interfaces")
1697
1698 logger.info("[DUT: %s]: Verify pim neighbor after pim" " neighbor clear", dut)
1699
1700 found = False
1701
1702 # Waiting for maximum 60 sec
1703 fail_intf = []
1704 for retry in range(1, 13):
1705 logger.info("[DUT: %s]: Waiting for 5 sec for PIM neighbors" " to come up", dut)
1706 sleep(5)
1707 run_json_after = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True)
1708 found = True
1709 for pim_intf in nh_before_clear.keys():
1710 if pim_intf not in run_json_after or not run_json_after[pim_intf]:
1711 found = False
1712 fail_intf.append(pim_intf)
1713
1714 if found is True:
1715 break
1716 else:
1717 errormsg = (
1718 "[DUT: %s]: pim neighborship is not formed for %s"
1719 "after clear_ip_pim_interfaces %s [FAILED!!]",
1720 dut,
1721 fail_intf,
1722 )
1723 return errormsg
1724
1725 for key, value in run_json_after.items():
1726 if bool(value):
1727 for _key, _value in value.items():
1728 nh_after_clear[key] = _value["upTime"]
1729
1730 # Verify uptime for neighbors
1731 for pim_intf in nh_before_clear.keys():
1732 d1 = datetime.datetime.strptime(nh_before_clear[pim_intf], "%H:%M:%S")
1733 d2 = datetime.datetime.strptime(nh_after_clear[pim_intf], "%H:%M:%S")
1734 if d2 >= d1:
1735 errormsg = (
1736 "[DUT: %s]: PIM neighborship is not cleared for",
1737 " interface %s [FAILED!!]",
1738 dut,
1739 pim_intf,
1740 )
1741
1742 logger.info("[DUT: %s]: PIM neighborship is cleared [PASSED!!]")
1743
1744 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1745
1746 return True
1747
1748
1749def clear_ip_igmp_interfaces(tgen, dut):
1750 """
1751 Clear ip igmp interfaces by running
1752 "clear ip igmp interfaces" cli
1753
1754 Parameters
1755 ----------
1756 * `tgen`: topogen object
1757 * `dut`: device under test
1758
1759 Usage
1760 -----
1761 dut = "r1"
1762 result = clear_ip_igmp_interfaces(tgen, dut)
1763 Returns
1764 -------
1765 errormsg(str) or True
1766 """
1767
1768 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
1769
1770 group_before_clear = {}
1771 group_after_clear = {}
1772
1773 rnode = tgen.routers()[dut]
1774
1775 logger.info("[DUT: %s]: IGMP group uptime before clear" " igmp groups:", dut)
1776 igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True)
1777
1778 total_groups_before_clear = igmp_json["totalGroups"]
1779
1780 for key, value in igmp_json.items():
1781 if type(value) is not dict:
1782 continue
1783
1784 groups = value["groups"]
1785 group = groups[0]["group"]
1786 uptime = groups[0]["uptime"]
1787 group_before_clear[group] = uptime
1788
1789 logger.info("[DUT: %s]: Clearing ip igmp interfaces", dut)
1790 result = run_frr_cmd(rnode, "clear ip igmp interfaces")
1791
1792 # Waiting for maximum 60 sec
1793 for retry in range(1, 13):
1794 logger.info(
1795 "[DUT: %s]: Waiting for 5 sec for igmp interfaces" " to come up", dut
1796 )
1797 sleep(5)
1798 igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True)
1799
1800 total_groups_after_clear = igmp_json["totalGroups"]
1801
1802 if total_groups_before_clear == total_groups_after_clear:
1803 break
1804
1805 for key, value in igmp_json.items():
1806 if type(value) is not dict:
1807 continue
1808
1809 groups = value["groups"]
1810 group = groups[0]["group"]
1811 uptime = groups[0]["uptime"]
1812 group_after_clear[group] = uptime
1813
1814 # Verify uptime for groups
1815 for group in group_before_clear.keys():
1816 d1 = datetime.datetime.strptime(group_before_clear[group], "%H:%M:%S")
1817 d2 = datetime.datetime.strptime(group_after_clear[group], "%H:%M:%S")
1818 if d2 >= d1:
1819 errormsg = ("[DUT: %s]: IGMP group is not cleared", " [FAILED!!]", dut)
1820
1821 logger.info("[DUT: %s]: IGMP group is cleared [PASSED!!]")
1822
1823 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1824
1825 return True
1826
1827
ed776e38 1828@retry(retry_timeout=20)
3c41ebf8 1829def clear_ip_mroute_verify(tgen, dut, expected=True):
e8cd26fd 1830 """
1831 Clear ip mroute by running "clear ip mroute" cli and verify
1832 mroutes are up again after mroute clear
1833
1834 Parameters
1835 ----------
1836 * `tgen`: topogen object
1837 * `dut`: Device Under Test
3c41ebf8
KK
1838 * `expected` : expected results from API, by-default True
1839
e8cd26fd 1840 Usage
1841 -----
1842
1843 result = clear_ip_mroute_verify(tgen, dut)
1844
1845 Returns
1846 -------
1847 errormsg(str) or True
1848 """
1849
1850 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
1851
1852 mroute_before_clear = {}
1853 mroute_after_clear = {}
1854
1855 rnode = tgen.routers()[dut]
1856
e8cd26fd 1857 logger.info("[DUT: %s]: IP mroutes uptime before clear", dut)
1858 mroute_json_1 = run_frr_cmd(rnode, "show ip mroute json", isjson=True)
1859
1860 for group in mroute_json_1.keys():
1861 mroute_before_clear[group] = {}
1862 for key in mroute_json_1[group].keys():
1863 for _key, _value in mroute_json_1[group][key]["oil"].items():
1864 if _key != "pimreg":
1865 mroute_before_clear[group][key] = _value["upTime"]
1866
1867 logger.info("[DUT: %s]: Clearing ip mroute", dut)
1868 result = run_frr_cmd(rnode, "clear ip mroute")
1869
1870 # RFC 3376: 8.2. Query Interval - Default: 125 seconds
1871 # So waiting for maximum 130 sec to get the igmp report
1872 for retry in range(1, 26):
1873 logger.info("[DUT: %s]: Waiting for 2 sec for mroutes" " to come up", dut)
1874 sleep(5)
1875 keys_json1 = mroute_json_1.keys()
1876 mroute_json_2 = run_frr_cmd(rnode, "show ip mroute json", isjson=True)
1877
1878 if bool(mroute_json_2):
1879 keys_json2 = mroute_json_2.keys()
1880
1881 for group in mroute_json_2.keys():
1882 flag = False
1883 for key in mroute_json_2[group].keys():
1884 if "oil" not in mroute_json_2[group]:
1885 continue
1886
1887 for _key, _value in mroute_json_2[group][key]["oil"].items():
1888 if _key != "pimreg" and keys_json1 == keys_json2:
1889 break
1890 flag = True
1891 if flag:
1892 break
1893 else:
1894 continue
1895
1896 for group in mroute_json_2.keys():
1897 mroute_after_clear[group] = {}
1898 for key in mroute_json_2[group].keys():
1899 for _key, _value in mroute_json_2[group][key]["oil"].items():
1900 if _key != "pimreg":
1901 mroute_after_clear[group][key] = _value["upTime"]
1902
1903 # Verify uptime for mroute
1904 for group in mroute_before_clear.keys():
1905 for source in mroute_before_clear[group].keys():
1906 if set(mroute_before_clear[group]) != set(mroute_after_clear[group]):
1907 errormsg = (
1908 "[DUT: %s]: mroute (%s, %s) has not come"
1909 " up after mroute clear [FAILED!!]" % (dut, source, group)
1910 )
1911 return errormsg
1912
1913 d1 = datetime.datetime.strptime(
1914 mroute_before_clear[group][source], "%H:%M:%S"
1915 )
1916 d2 = datetime.datetime.strptime(
1917 mroute_after_clear[group][source], "%H:%M:%S"
1918 )
1919 if d2 >= d1:
1920 errormsg = "[DUT: %s]: IP mroute is not cleared" " [FAILED!!]" % (dut)
1921
1922 logger.info("[DUT: %s]: IP mroute is cleared [PASSED!!]", dut)
1923
1924 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1925
1926 return True
1927
1928
1929def clear_ip_mroute(tgen, dut=None):
1930 """
1931 Clear ip mroute by running "clear ip mroute" cli
1932
1933 Parameters
1934 ----------
1935 * `tgen`: topogen object
1936 * `dut`: device under test, default None
1937
1938 Usage
1939 -----
1940 clear_ip_mroute(tgen, dut)
1941 """
1942
1943 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
1944
1945 router_list = tgen.routers()
1946 for router, rnode in router_list.items():
1947 if dut is not None and router != dut:
1948 continue
1949
1950 logger.debug("[DUT: %s]: Clearing ip mroute", router)
1951 rnode.vtysh_cmd("clear ip mroute")
1952
1953 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
1954
1955
1956def reconfig_interfaces(tgen, topo, senderRouter, receiverRouter, packet=None):
1957 """
1958 Configure interface ip for sender and receiver routers
1959 as per bsr packet
1960
1961 Parameters
1962 ----------
1963 * `tgen` : Topogen object
1964 * `topo` : json file data
1965 * `senderRouter` : Sender router
1966 * `receiverRouter` : Receiver router
1967 * `packet` : BSR packet in raw format
1968
1969 Returns
1970 -------
1971 True or False
1972 """
1973 result = False
1974 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
1975
1976 try:
1977 config_data = []
1978
1979 src_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["src_ip"]
1980 dest_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["dest_ip"]
1981
1982 for destLink, data in topo["routers"][senderRouter]["links"].items():
1983 if "type" in data and data["type"] == "loopback":
1984 continue
1985
1986 if "pim" in data and data["pim"] == "enable":
1987 sender_interface = data["interface"]
1988 sender_interface_ip = data["ipv4"]
1989
1990 config_data.append("interface {}".format(sender_interface))
1991 config_data.append("no ip address {}".format(sender_interface_ip))
1992 config_data.append("ip address {}".format(src_ip))
1993
1994 result = create_common_configuration(
1995 tgen, senderRouter, config_data, "interface_config"
1996 )
1997 if result is not True:
1998 return False
1999
2000 config_data = []
2001 links = topo["routers"][destLink]["links"]
2002 pim_neighbor = {key: links[key] for key in [senderRouter]}
2003
2004 data = pim_neighbor[senderRouter]
2005 if "type" in data and data["type"] == "loopback":
2006 continue
2007
2008 if "pim" in data and data["pim"] == "enable":
2009 receiver_interface = data["interface"]
2010 receiver_interface_ip = data["ipv4"]
2011
2012 config_data.append("interface {}".format(receiver_interface))
2013 config_data.append("no ip address {}".format(receiver_interface_ip))
2014 config_data.append("ip address {}".format(dest_ip))
2015
2016 result = create_common_configuration(
2017 tgen, receiverRouter, config_data, "interface_config"
2018 )
2019 if result is not True:
2020 return False
2021
2022 except InvalidCLIError:
2023 # Traceback
2024 errormsg = traceback.format_exc()
2025 logger.error(errormsg)
2026 return errormsg
2027
2028 logger.debug("Exiting lib API: reconfig_interfaces()")
2029 return result
2030
2031
2032def add_rp_interfaces_and_pim_config(tgen, topo, interface, rp, rp_mapping):
2033 """
2034 Add physical interfaces tp RP for all the RPs
2035
2036 Parameters
2037 ----------
2038 * `tgen` : Topogen object
2039 * `topo` : json file data
2040 * `interface` : RP interface
2041 * `rp` : rp for given topology
2042 * `rp_mapping` : dictionary of all groups and RPs
2043
2044 Returns
2045 -------
2046 True or False
2047 """
2048 result = False
2049 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
2050
2051 try:
2052 config_data = []
2053
2054 for group, rp_list in rp_mapping.items():
2055 for _rp in rp_list:
2056 config_data.append("interface {}".format(interface))
2057 config_data.append("ip address {}".format(_rp))
2058 config_data.append("ip pim")
2059
4f99894d 2060 # Why not config just once, why per group?
e8cd26fd 2061 result = create_common_configuration(
2062 tgen, rp, config_data, "interface_config"
2063 )
2064 if result is not True:
2065 return False
2066
2067 except InvalidCLIError:
2068 # Traceback
2069 errormsg = traceback.format_exc()
2070 logger.error(errormsg)
2071 return errormsg
2072
8ab46256 2073 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
e8cd26fd 2074 return result
2075
2076
49581587 2077def scapy_send_bsr_raw_packet(tgen, topo, senderRouter, receiverRouter, packet=None):
e8cd26fd 2078 """
2079 Using scapy Raw() method to send BSR raw packet from one FRR
2080 to other
2081
2082 Parameters:
2083 -----------
2084 * `tgen` : Topogen object
2085 * `topo` : json file data
2086 * `senderRouter` : Sender router
2087 * `receiverRouter` : Receiver router
2088 * `packet` : BSR packet in raw format
e8cd26fd 2089
2090 returns:
2091 --------
2092 errormsg or True
2093 """
2094
2095 global CWD
2096 result = ""
2097 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
2098
49581587
CH
2099 python3_path = tgen.net.get_exec_path(["python3", "python"])
2100 script_path = os.path.join(CWD, "send_bsr_packet.py")
2101 node = tgen.net[senderRouter]
e8cd26fd 2102
2103 for destLink, data in topo["routers"][senderRouter]["links"].items():
2104 if "type" in data and data["type"] == "loopback":
2105 continue
2106
2107 if "pim" in data and data["pim"] == "enable":
2108 sender_interface = data["interface"]
2109
2110 packet = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["data"]
2111
49581587
CH
2112 cmd = [
2113 python3_path,
2114 script_path,
2115 packet,
2116 sender_interface,
2117 "--interval=1",
2118 "--count=1",
2119 ]
e8cd26fd 2120 logger.info("Scapy cmd: \n %s", cmd)
49581587 2121 node.cmd_raises(cmd)
e8cd26fd 2122
2123 logger.debug("Exiting lib API: scapy_send_bsr_raw_packet")
2124 return True
2125
2126
2127def find_rp_from_bsrp_info(tgen, dut, bsr, grp=None):
2128 """
2129 Find which RP is having lowest prioriy and returns rp IP
2130
2131 Parameters
2132 ----------
2133 * `tgen`: topogen object
2134 * `dut`: device under test
2135 * `bsr`: BSR address
2136 * 'grp': Group Address
2137
2138 Usage
2139 -----
2140 dut = "r1"
2141 result = verify_pim_rp_info(tgen, dut, bsr)
2142
2143 Returns:
2144 dictionary: group and RP, which has to be installed as per
2145 lowest priority or highest priority
2146 """
2147
2148 rp_details = {}
2149 rnode = tgen.routers()[dut]
2150
2151 logger.info("[DUT: %s]: Fetching rp details from bsrp-info", dut)
2152 bsrp_json = run_frr_cmd(rnode, "show ip pim bsrp-info json", isjson=True)
2153
2154 if grp not in bsrp_json:
2155 return {}
2156
2157 for group, rp_data in bsrp_json.items():
2158 if group == "BSR Address" and bsrp_json["BSR Address"] == bsr:
2159 continue
2160
2161 if group != grp:
2162 continue
2163
2164 rp_priority = {}
2165 rp_hash = {}
2166
2167 for rp, value in rp_data.items():
2168 if rp == "Pending RP count":
2169 continue
2170 rp_priority[value["Rp Address"]] = value["Rp Priority"]
2171 rp_hash[value["Rp Address"]] = value["Hash Val"]
2172
2173 priority_dict = dict(zip(rp_priority.values(), rp_priority.keys()))
2174 hash_dict = dict(zip(rp_hash.values(), rp_hash.keys()))
2175
2176 # RP with lowest priority
2177 if len(priority_dict) != 1:
2178 rp_p, lowest_priority = sorted(rp_priority.items(), key=lambda x: x[1])[0]
2179 rp_details[group] = rp_p
2180
2181 # RP with highest hash value
2182 if len(priority_dict) == 1:
2183 rp_h, highest_hash = sorted(rp_hash.items(), key=lambda x: x[1])[-1]
2184 rp_details[group] = rp_h
2185
2186 # RP with highest IP address
2187 if len(priority_dict) == 1 and len(hash_dict) == 1:
2188 rp_details[group] = sorted(rp_priority.keys())[-1]
2189
2190 return rp_details
2191
2192
ed776e38 2193@retry(retry_timeout=12)
a53c08bc
CH
2194def verify_pim_grp_rp_source(
2195 tgen, topo, dut, grp_addr, rp_source, rpadd=None, expected=True
2196):
e8cd26fd 2197 """
2198 Verify pim rp info by running "show ip pim rp-info" cli
2199
2200 Parameters
2201 ----------
2202 * `tgen`: topogen object
2203 * `topo`: JSON file handler
2204 * `dut`: device under test
2205 * `grp_addr`: IGMP group address
2206 * 'rp_source': source from which rp installed
2207 * 'rpadd': rp address
3c41ebf8 2208 * `expected` : expected results from API, by-default True
e8cd26fd 2209
2210 Usage
2211 -----
2212 dut = "r1"
2213 group_address = "225.1.1.1"
2214 rp_source = "BSR"
2215 result = verify_pim_rp_and_source(tgen, topo, dut, group_address, rp_source)
2216
2217 Returns
2218 -------
2219 errormsg(str) or True
2220 """
2221
2222 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
2223
2224 if dut not in tgen.routers():
2225 return False
2226
2227 rnode = tgen.routers()[dut]
2228
2229 logger.info("[DUT: %s]: Verifying ip rp info", dut)
2230 show_ip_rp_info_json = run_frr_cmd(rnode, "show ip pim rp-info json", isjson=True)
2231
2232 if rpadd != None:
2233 rp_json = show_ip_rp_info_json[rpadd]
2234 if rp_json[0]["group"] == grp_addr:
2235 if rp_json[0]["source"] == rp_source:
2236 logger.info(
2237 "[DUT %s]: Verifying Group and rp_source [PASSED]"
2238 "Found Expected: %s, %s"
2239 % (dut, rp_json[0]["group"], rp_json[0]["source"])
2240 )
2241 return True
2242 else:
2243 errormsg = (
2244 "[DUT %s]: Verifying Group and rp_source [FAILED]"
2245 "Expected (%s, %s) "
2246 "Found (%s, %s)"
2247 % (
2248 dut,
2249 grp_addr,
2250 rp_source,
2251 rp_json[0]["group"],
2252 rp_json[0]["source"],
2253 )
2254 )
2255 return errormsg
2256 errormsg = (
2257 "[DUT %s]: Verifying Group and rp_source [FAILED]"
2258 "Expected: %s, %s but not found" % (dut, grp_addr, rp_source)
2259 )
2260 return errormsg
2261
2262 for rp in show_ip_rp_info_json:
2263 rp_json = show_ip_rp_info_json[rp]
2264 logger.info("%s", rp_json)
2265 if rp_json[0]["group"] == grp_addr:
2266 if rp_json[0]["source"] == rp_source:
2267 logger.info(
2268 "[DUT %s]: Verifying Group and rp_source [PASSED]"
2269 "Found Expected: %s, %s"
2270 % (dut, rp_json[0]["group"], rp_json[0]["source"])
2271 )
2272 return True
2273 else:
2274 errormsg = (
2275 "[DUT %s]: Verifying Group and rp_source [FAILED]"
2276 "Expected (%s, %s) "
2277 "Found (%s, %s)"
2278 % (
2279 dut,
2280 grp_addr,
2281 rp_source,
2282 rp_json[0]["group"],
2283 rp_json[0]["source"],
2284 )
2285 )
2286 return errormsg
2287
2288 errormsg = (
2289 "[DUT %s]: Verifying Group and rp_source [FAILED]"
2290 "Expected: %s, %s but not found" % (dut, grp_addr, rp_source)
2291 )
2292
2293 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
2294
2295 return errormsg
2296
2297
ed776e38 2298@retry(retry_timeout=60)
3c41ebf8 2299def verify_pim_bsr(tgen, topo, dut, bsr_ip, expected=True):
e8cd26fd 2300 """
2301 Verify all PIM interface are up and running, config is verified
2302 using "show ip pim interface" cli
2303
2304 Parameters
2305 ----------
2306 * `tgen`: topogen object
2307 * `topo` : json file data
2308 * `dut` : device under test
2309 * 'bsr' : bsr ip to be verified
3c41ebf8 2310 * `expected` : expected results from API, by-default True
e8cd26fd 2311
2312 Usage
2313 -----
2314 result = verify_pim_bsr(tgen, topo, dut, bsr_ip)
2315
2316 Returns
2317 -------
2318 errormsg(str) or True
2319 """
2320
2321 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
2322
2323 for router in tgen.routers():
2324 if router != dut:
2325 continue
2326
2327 logger.info("[DUT: %s]: Verifying PIM bsr status:", dut)
2328
2329 rnode = tgen.routers()[dut]
2330 pim_bsr_json = rnode.vtysh_cmd("show ip pim bsr json", isjson=True)
2331
2332 logger.info("show_ip_pim_bsr_json: \n %s", pim_bsr_json)
2333
2334 # Verifying PIM bsr
2335 if pim_bsr_json["bsr"] != bsr_ip:
2336 errormsg = (
2337 "[DUT %s]:"
2338 "bsr status: not found"
2339 "[FAILED]!! Expected : %s, Found : %s"
2340 % (dut, bsr_ip, pim_bsr_json["bsr"])
2341 )
2342 return errormsg
2343
2344 logger.info(
2345 "[DUT %s]:" " bsr status: found, Address :%s" " [PASSED]!!",
2346 dut,
2347 pim_bsr_json["bsr"],
2348 )
2349
2350 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
2351 return True
2352
2353
ed776e38 2354@retry(retry_timeout=60)
a53c08bc
CH
2355def verify_ip_pim_upstream_rpf(
2356 tgen, topo, dut, interface, group_addresses, rp=None, expected=True
2357):
e8cd26fd 2358 """
2359 Verify IP PIM upstream rpf, config is verified
2360 using "show ip pim neighbor" cli
2361
2362 Parameters
2363 ----------
2364 * `tgen`: topogen object
2365 * `topo` : json file data
2366 * `dut` : devuce under test
2367 * `interface` : upstream interface
2368 * `group_addresses` : list of group address for which upstream info
2369 needs to be checked
2370 * `rp` : RP address
3c41ebf8 2371 * `expected` : expected results from API, by-default True
e8cd26fd 2372
2373 Usage
2374 -----
2375 result = verify_ip_pim_upstream_rpf(gen, topo, dut, interface,
2376 group_addresses, rp=None)
2377
2378 Returns
2379 -------
2380 errormsg(str) or True
2381 """
2382
2383 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
2384
2385 if "pim" in topo["routers"][dut]:
2386
2387 logger.info("[DUT: %s]: Verifying ip pim upstream rpf:", dut)
2388
2389 rnode = tgen.routers()[dut]
2390 show_ip_pim_upstream_rpf_json = rnode.vtysh_cmd(
2391 "show ip pim upstream-rpf json", isjson=True
2392 )
2393
2394 logger.info(
2395 "show_ip_pim_upstream_rpf_json: \n %s", show_ip_pim_upstream_rpf_json
2396 )
2397
2398 if type(group_addresses) is not list:
2399 group_addresses = [group_addresses]
2400
2401 for grp_addr in group_addresses:
2402 for destLink, data in topo["routers"][dut]["links"].items():
2403 if "type" in data and data["type"] == "loopback":
2404 continue
2405
2406 if "pim" not in topo["routers"][destLink]:
2407 continue
2408
2409 # Verify RP info
2410 if rp is None:
2411 rp_details = find_rp_details(tgen, topo)
2412 else:
660c59ed 2413 rp_details = {dut: rp}
e8cd26fd 2414
2415 if dut in rp_details:
2416 pim_nh_intf_ip = topo["routers"][dut]["links"]["lo"]["ipv4"].split(
2417 "/"
2418 )[0]
2419 else:
2420 if destLink not in interface:
2421 continue
2422
2423 links = topo["routers"][destLink]["links"]
2424 pim_neighbor = {key: links[key] for key in [dut]}
2425
2426 data = pim_neighbor[dut]
2427 if "pim" in data and data["pim"] == "enable":
2428 pim_nh_intf_ip = data["ipv4"].split("/")[0]
2429
2430 upstream_rpf_json = show_ip_pim_upstream_rpf_json[grp_addr]["*"]
2431
2432 # Verifying ip pim upstream rpf
2433 if (
2434 upstream_rpf_json["rpfInterface"] == interface
2435 and upstream_rpf_json["ribNexthop"] != pim_nh_intf_ip
2436 ):
2437 errormsg = (
2438 "[DUT %s]: Verifying group: %s, "
2439 "rpf interface: %s, "
2440 " rib Nexthop check [FAILED]!!"
2441 "Expected: %s, Found: %s"
2442 % (
2443 dut,
2444 grp_addr,
2445 interface,
2446 pim_nh_intf_ip,
2447 upstream_rpf_json["ribNexthop"],
2448 )
2449 )
2450 return errormsg
2451
2452 logger.info(
2453 "[DUT %s]: Verifying group: %s,"
2454 " rpf interface: %s, "
2455 " rib Nexthop: %s [PASSED]!!",
2456 dut,
2457 grp_addr,
2458 interface,
2459 pim_nh_intf_ip,
2460 )
2461
2462 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
2463 return True
2464
2465
2466def enable_disable_pim_unicast_bsm(tgen, router, intf, enable=True):
2467 """
2468 Helper API to enable or disable pim bsm on interfaces
2469
2470 Parameters
2471 ----------
2472 * `tgen` : Topogen object
2473 * `router` : router id to be configured.
2474 * `intf` : Interface to be configured
2475 * `enable` : this flag denotes if config should be enabled or disabled
2476
2477 Returns
2478 -------
2479 True or False
2480 """
2481 result = False
2482 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
2483
2484 try:
2485 config_data = []
2486 cmd = "interface {}".format(intf)
2487 config_data.append(cmd)
2488
2489 if enable == True:
2490 config_data.append("ip pim unicast-bsm")
2491 else:
2492 config_data.append("no ip pim unicast-bsm")
2493
2494 result = create_common_configuration(
2495 tgen, router, config_data, "interface_config", build=False
2496 )
2497 if result is not True:
2498 return False
2499
2500 except InvalidCLIError:
2501 # Traceback
2502 errormsg = traceback.format_exc()
2503 logger.error(errormsg)
2504 return errormsg
2505
2506 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
2507 return result
2508
2509
2510def enable_disable_pim_bsm(tgen, router, intf, enable=True):
2511 """
2512 Helper API to enable or disable pim bsm on interfaces
2513
2514 Parameters
2515 ----------
2516 * `tgen` : Topogen object
2517 * `router` : router id to be configured.
2518 * `intf` : Interface to be configured
2519 * `enable` : this flag denotes if config should be enabled or disabled
2520
2521 Returns
2522 -------
2523 True or False
2524 """
2525 result = False
2526 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
2527
2528 try:
2529 config_data = []
2530 cmd = "interface {}".format(intf)
2531 config_data.append(cmd)
2532
2533 if enable is True:
2534 config_data.append("ip pim bsm")
2535 else:
2536 config_data.append("no ip pim bsm")
2537
2538 result = create_common_configuration(
2539 tgen, router, config_data, "interface_config", build=False
2540 )
2541 if result is not True:
2542 return False
2543
2544 except InvalidCLIError:
2545 # Traceback
2546 errormsg = traceback.format_exc()
2547 logger.error(errormsg)
2548 return errormsg
2549
2550 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
2551 return result
2552
2553
ed776e38 2554@retry(retry_timeout=60)
a53c08bc
CH
2555def verify_ip_pim_join(
2556 tgen, topo, dut, interface, group_addresses, src_address=None, expected=True
2557):
e8cd26fd 2558 """
2559 Verify ip pim join by running "show ip pim join" cli
2560
2561 Parameters
2562 ----------
2563 * `tgen`: topogen object
2564 * `topo`: JSON file handler
2565 * `dut`: device under test
2566 * `interface`: interface name, from which PIM join would come
2567 * `group_addresses`: IGMP group address
2568 * `src_address`: Source address
3c41ebf8 2569 * `expected` : expected results from API, by-default True
e8cd26fd 2570
2571 Usage
2572 -----
2573 dut = "r1"
2574 interface = "r1-r0-eth0"
2575 group_address = "225.1.1.1"
2576 result = verify_ip_pim_join(tgen, dut, star, group_address, interface)
2577
2578 Returns
2579 -------
2580 errormsg(str) or True
2581 """
2582 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
2583
2584 if dut not in tgen.routers():
2585 return False
2586
2587 rnode = tgen.routers()[dut]
2588
2589 logger.info("[DUT: %s]: Verifying pim join", dut)
2590 show_pim_join_json = run_frr_cmd(rnode, "show ip pim join json", isjson=True)
2591
2592 if type(group_addresses) is not list:
2593 group_addresses = [group_addresses]
2594
2595 for grp_addr in group_addresses:
2596 # Verify if IGMP is enabled in DUT
2597 if "igmp" not in topo["routers"][dut]:
2598 pim_join = True
2599 else:
2600 pim_join = False
2601
2602 interface_json = show_pim_join_json[interface]
2603
2604 grp_addr = grp_addr.split("/")[0]
2605 for source, data in interface_json[grp_addr].items():
2606
2607 # Verify pim join
2608 if pim_join:
2609 if data["group"] == grp_addr and data["channelJoinName"] == "JOIN":
2610 logger.info(
2611 "[DUT %s]: Verifying pim join for group: %s"
2612 "[PASSED]!! Found Expected: (%s)",
2613 dut,
2614 grp_addr,
2615 data["channelJoinName"],
2616 )
2617 else:
2618 errormsg = (
2619 "[DUT %s]: Verifying pim join for group: %s"
2620 "[FAILED]!! Expected: (%s) "
2621 "Found: (%s)" % (dut, grp_addr, "JOIN", data["channelJoinName"])
2622 )
2623 return errormsg
2624
2625 if not pim_join:
2626 if data["group"] == grp_addr and data["channelJoinName"] == "NOINFO":
2627 logger.info(
2628 "[DUT %s]: Verifying pim join for group: %s"
2629 "[PASSED]!! Found Expected: (%s)",
2630 dut,
2631 grp_addr,
2632 data["channelJoinName"],
2633 )
2634 else:
2635 errormsg = (
2636 "[DUT %s]: Verifying pim join for group: %s"
2637 "[FAILED]!! Expected: (%s) "
2638 "Found: (%s)"
2639 % (dut, grp_addr, "NOINFO", data["channelJoinName"])
2640 )
2641 return errormsg
2642
2643 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
2644 return True
2645
2646
ed776e38 2647@retry(retry_timeout=60)
3c41ebf8 2648def verify_igmp_config(tgen, input_dict, stats_return=False, expected=True):
e8cd26fd 2649 """
2650 Verify igmp interface details, verifying following configs:
2651 timerQueryInterval
2652 timerQueryResponseIntervalMsec
2653 lastMemberQueryCount
2654 timerLastMemberQueryMsec
2655
2656 Parameters
2657 ----------
2658 * `tgen`: topogen object
2659 * `input_dict` : Input dict data, required to verify
2660 timer
2661 * `stats_return`: If user wants API to return statistics
3c41ebf8 2662 * `expected` : expected results from API, by-default True
e8cd26fd 2663
2664 Usage
2665 -----
2666 input_dict ={
2667 "l1": {
2668 "igmp": {
2669 "interfaces": {
2670 "l1-i1-eth1": {
2671 "igmp": {
2672 "query": {
2673 "query-interval" : 200,
2674 "query-max-response-time" : 100
2675 },
2676 "statistics": {
2677 "queryV2" : 2,
2678 "reportV2" : 1
2679 }
2680 }
2681 }
2682 }
2683 }
2684 }
2685 }
2686 result = verify_igmp_config(tgen, input_dict, stats_return)
2687
2688 Returns
2689 -------
2690 errormsg(str) or True
2691 """
2692
2693 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
2694
2695 for dut in input_dict.keys():
2696 rnode = tgen.routers()[dut]
2697
2698 for interface, data in input_dict[dut]["igmp"]["interfaces"].items():
2699
2700 statistics = False
2701 report = False
2702 if "statistics" in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"]:
2703 statistics = True
2704 cmd = "show ip igmp statistics"
2705 else:
2706 cmd = "show ip igmp"
2707
2708 logger.info(
2709 "[DUT: %s]: Verifying IGMP interface %s detail:", dut, interface
2710 )
2711
2712 if statistics:
2713 if (
2714 "report"
2715 in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"][
2716 "statistics"
2717 ]
2718 ):
2719 report = True
2720
2721 if statistics and report:
2722 show_ip_igmp_intf_json = run_frr_cmd(
5aab262b 2723 rnode, "{} json".format(cmd), isjson=True
e8cd26fd 2724 )
2725 intf_detail_json = show_ip_igmp_intf_json["global"]
2726 else:
2727 show_ip_igmp_intf_json = run_frr_cmd(
2728 rnode, "{} interface {} json".format(cmd, interface), isjson=True
2729 )
2730
2731 if not report:
2732 if interface not in show_ip_igmp_intf_json:
2733 errormsg = (
2734 "[DUT %s]: IGMP interface: %s "
2735 " is not present in CLI output "
2736 "[FAILED]!! " % (dut, interface)
2737 )
2738 return errormsg
2739
2740 else:
2741 intf_detail_json = show_ip_igmp_intf_json[interface]
2742
2743 if stats_return:
2744 igmp_stats = {}
2745
2746 if "statistics" in data["igmp"]:
2747 if stats_return:
2748 igmp_stats["statistics"] = {}
2749 for query, value in data["igmp"]["statistics"].items():
2750 if query == "queryV2":
2751 # Verifying IGMP interface queryV2 statistics
2752 if stats_return:
2753 igmp_stats["statistics"][query] = intf_detail_json[
2754 "queryV2"
2755 ]
2756
2757 else:
2758 if intf_detail_json["queryV2"] != value:
2759 errormsg = (
2760 "[DUT %s]: IGMP interface: %s "
2761 " queryV2 statistics verification "
2762 "[FAILED]!! Expected : %s,"
2763 " Found : %s"
2764 % (
2765 dut,
2766 interface,
2767 value,
2768 intf_detail_json["queryV2"],
2769 )
2770 )
2771 return errormsg
2772
2773 logger.info(
2774 "[DUT %s]: IGMP interface: %s "
2775 "queryV2 statistics is %s",
2776 dut,
2777 interface,
2778 value,
2779 )
2780
2781 if query == "reportV2":
2782 # Verifying IGMP interface timerV2 statistics
2783 if stats_return:
2784 igmp_stats["statistics"][query] = intf_detail_json[
2785 "reportV2"
2786 ]
2787
2788 else:
2789 if intf_detail_json["reportV2"] <= value:
2790 errormsg = (
2791 "[DUT %s]: IGMP reportV2 "
2792 "statistics verification "
2793 "[FAILED]!! Expected : %s "
2794 "or more, Found : %s"
2795 % (
2796 dut,
2797 interface,
2798 value,
e8cd26fd 2799 )
2800 )
2801 return errormsg
2802
2803 logger.info(
2804 "[DUT %s]: IGMP reportV2 " "statistics is %s",
2805 dut,
2806 intf_detail_json["reportV2"],
2807 )
2808
2809 if "query" in data["igmp"]:
2810 for query, value in data["igmp"]["query"].items():
2811 if query == "query-interval":
2812 # Verifying IGMP interface query interval timer
2813 if intf_detail_json["timerQueryInterval"] != value:
2814 errormsg = (
2815 "[DUT %s]: IGMP interface: %s "
2816 " query-interval verification "
2817 "[FAILED]!! Expected : %s,"
2818 " Found : %s"
2819 % (
2820 dut,
2821 interface,
2822 value,
2823 intf_detail_json["timerQueryInterval"],
2824 )
2825 )
2826 return errormsg
2827
2828 logger.info(
2829 "[DUT %s]: IGMP interface: %s " "query-interval is %s",
2830 dut,
2831 interface,
2832 value,
2833 )
2834
2835 if query == "query-max-response-time":
2836 # Verifying IGMP interface query max response timer
2837 if (
2838 intf_detail_json["timerQueryResponseIntervalMsec"]
2839 != value * 100
2840 ):
2841 errormsg = (
2842 "[DUT %s]: IGMP interface: %s "
2843 "query-max-response-time "
2844 "verification [FAILED]!!"
2845 " Expected : %s, Found : %s"
2846 % (
2847 dut,
2848 interface,
2849 value * 1000,
2850 intf_detail_json["timerQueryResponseIntervalMsec"],
2851 )
2852 )
2853 return errormsg
2854
2855 logger.info(
2856 "[DUT %s]: IGMP interface: %s "
2857 "query-max-response-time is %s ms",
2858 dut,
2859 interface,
2860 value * 100,
2861 )
2862
2863 if query == "last-member-query-count":
2864 # Verifying IGMP interface last member query count
2865 if intf_detail_json["lastMemberQueryCount"] != value:
2866 errormsg = (
2867 "[DUT %s]: IGMP interface: %s "
2868 "last-member-query-count "
2869 "verification [FAILED]!!"
2870 " Expected : %s, Found : %s"
2871 % (
2872 dut,
2873 interface,
2874 value,
2875 intf_detail_json["lastMemberQueryCount"],
2876 )
2877 )
2878 return errormsg
2879
2880 logger.info(
2881 "[DUT %s]: IGMP interface: %s "
2882 "last-member-query-count is %s ms",
2883 dut,
2884 interface,
2885 value * 1000,
2886 )
2887
2888 if query == "last-member-query-interval":
2889 # Verifying IGMP interface last member query interval
2890 if (
2891 intf_detail_json["timerLastMemberQueryMsec"]
2892 != value * 100 * intf_detail_json["lastMemberQueryCount"]
2893 ):
2894 errormsg = (
2895 "[DUT %s]: IGMP interface: %s "
2896 "last-member-query-interval "
2897 "verification [FAILED]!!"
2898 " Expected : %s, Found : %s"
2899 % (
2900 dut,
2901 interface,
2902 value * 1000,
2903 intf_detail_json["timerLastMemberQueryMsec"],
2904 )
2905 )
2906 return errormsg
2907
2908 logger.info(
2909 "[DUT %s]: IGMP interface: %s "
2910 "last-member-query-interval is %s ms",
2911 dut,
2912 interface,
2913 value * intf_detail_json["lastMemberQueryCount"] * 100,
2914 )
2915
2916 if "version" in data["igmp"]:
2917 # Verifying IGMP interface state is up
2918 if intf_detail_json["state"] != "up":
2919 errormsg = (
2920 "[DUT %s]: IGMP interface: %s "
2921 " state: %s verification "
2922 "[FAILED]!!" % (dut, interface, intf_detail_json["state"])
2923 )
2924 return errormsg
2925
2926 logger.info(
2927 "[DUT %s]: IGMP interface: %s " "state: %s",
2928 dut,
2929 interface,
2930 intf_detail_json["state"],
2931 )
2932
2933 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
2934 return True if stats_return == False else igmp_stats
2935
2936
ed776e38 2937@retry(retry_timeout=60)
3c41ebf8 2938def verify_pim_config(tgen, input_dict, expected=True):
e8cd26fd 2939 """
2940 Verify pim interface details, verifying following configs:
2941 drPriority
2942 helloPeriod
2943 helloReceived
2944 helloSend
2945 drAddress
2946
2947 Parameters
2948 ----------
2949 * `tgen`: topogen object
2950 * `input_dict` : Input dict data, required to verify
2951 timer
3c41ebf8 2952 * `expected` : expected results from API, by-default True
e8cd26fd 2953
2954 Usage
2955 -----
2956 input_dict ={
2957 "l1": {
2958 "igmp": {
2959 "interfaces": {
2960 "l1-i1-eth1": {
2961 "pim": {
2962 "drPriority" : 10,
2963 "helloPeriod" : 5
2964 }
2965 }
2966 }
2967 }
2968 }
2969 }
2970 }
2971 result = verify_pim_config(tgen, input_dict)
2972
2973 Returns
2974 -------
2975 errormsg(str) or True
2976 """
2977
2978 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
2979
2980 for dut in input_dict.keys():
2981 rnode = tgen.routers()[dut]
2982
2983 for interface, data in input_dict[dut]["pim"]["interfaces"].items():
2984
2985 logger.info("[DUT: %s]: Verifying PIM interface %s detail:", dut, interface)
2986
2987 show_ip_igmp_intf_json = run_frr_cmd(
2988 rnode, "show ip pim interface {} json".format(interface), isjson=True
2989 )
2990
2991 if interface not in show_ip_igmp_intf_json:
2992 errormsg = (
2993 "[DUT %s]: PIM interface: %s "
2994 " is not present in CLI output "
2995 "[FAILED]!! " % (dut, interface)
2996 )
2997 return errormsg
2998
2999 intf_detail_json = show_ip_igmp_intf_json[interface]
3000
3001 for config, value in data.items():
3002 if config == "helloPeriod":
3003 # Verifying PIM interface helloPeriod
3004 if intf_detail_json["helloPeriod"] != value:
3005 errormsg = (
3006 "[DUT %s]: PIM interface: %s "
3007 " helloPeriod verification "
3008 "[FAILED]!! Expected : %s,"
3009 " Found : %s"
3010 % (dut, interface, value, intf_detail_json["helloPeriod"])
3011 )
3012 return errormsg
3013
3014 logger.info(
3015 "[DUT %s]: PIM interface: %s " "helloPeriod is %s",
3016 dut,
3017 interface,
3018 value,
3019 )
3020
3021 if config == "drPriority":
3022 # Verifying PIM interface drPriority
3023 if intf_detail_json["drPriority"] != value:
3024 errormsg = (
3025 "[DUT %s]: PIM interface: %s "
3026 " drPriority verification "
3027 "[FAILED]!! Expected : %s,"
3028 " Found : %s"
3029 % (dut, interface, value, intf_detail_json["drPriority"])
3030 )
3031 return errormsg
3032
3033 logger.info(
3034 "[DUT %s]: PIM interface: %s " "drPriority is %s",
3035 dut,
3036 interface,
3037 value,
3038 )
3039
3040 if config == "drAddress":
3041 # Verifying PIM interface drAddress
3042 if intf_detail_json["drAddress"] != value:
3043 errormsg = (
3044 "[DUT %s]: PIM interface: %s "
3045 " drAddress verification "
3046 "[FAILED]!! Expected : %s,"
3047 " Found : %s"
3048 % (dut, interface, value, intf_detail_json["drAddress"])
3049 )
3050 return errormsg
3051
3052 logger.info(
3053 "[DUT %s]: PIM interface: %s " "drAddress is %s",
3054 dut,
3055 interface,
3056 value,
3057 )
3058
3059 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
3060 return True
3061
3062
ed776e38 3063@retry(retry_timeout=40)
3c41ebf8 3064def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=True):
e8cd26fd 3065 """
3066 Verify multicast traffic by running
3067 "show multicast traffic count json" cli
3068
3069 Parameters
3070 ----------
3071 * `tgen`: topogen object
3072 * `input_dict(dict)`: defines DUT, what and for which interfaces
3073 traffic needs to be verified
3074 * `return_traffic`: returns traffic stats
3c41ebf8
KK
3075 * `expected` : expected results from API, by-default True
3076
e8cd26fd 3077 Usage
3078 -----
3079 input_dict = {
3080 "r1": {
3081 "traffic_received": ["r1-r0-eth0"],
3082 "traffic_sent": ["r1-r0-eth0"]
3083 }
3084 }
3085
3086 result = verify_multicast_traffic(tgen, input_dict)
3087
3088 Returns
3089 -------
3090 errormsg(str) or True
3091 """
3092
3093 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
3094
3095 traffic_dict = {}
3096 for dut in input_dict.keys():
3097 if dut not in tgen.routers():
3098 continue
3099
3100 rnode = tgen.routers()[dut]
3101
3102 logger.info("[DUT: %s]: Verifying multicast " "traffic", dut)
3103
3104 show_multicast_traffic_json = run_frr_cmd(
3105 rnode, "show ip multicast count json", isjson=True
3106 )
3107
3108 for traffic_type, interfaces in input_dict[dut].items():
3109 traffic_dict[traffic_type] = {}
3110 if traffic_type == "traffic_received":
3111 for interface in interfaces:
3112 traffic_dict[traffic_type][interface] = {}
3113 interface_json = show_multicast_traffic_json[interface]
3114
3115 if interface_json["pktsIn"] == 0 and interface_json["bytesIn"] == 0:
3116 errormsg = (
3117 "[DUT %s]: Multicast traffic is "
3118 "not received on interface %s "
3119 "PktsIn: %s, BytesIn: %s "
3120 "[FAILED]!!"
3121 % (
3122 dut,
3123 interface,
3124 interface_json["pktsIn"],
3125 interface_json["bytesIn"],
3126 )
3127 )
3128 return errormsg
3129
3130 elif (
3131 interface_json["pktsIn"] != 0 and interface_json["bytesIn"] != 0
3132 ):
3133
3134 traffic_dict[traffic_type][interface][
3135 "pktsIn"
3136 ] = interface_json["pktsIn"]
3137 traffic_dict[traffic_type][interface][
3138 "bytesIn"
3139 ] = interface_json["bytesIn"]
3140
3141 logger.info(
3142 "[DUT %s]: Multicast traffic is "
3143 "received on interface %s "
3144 "PktsIn: %s, BytesIn: %s "
3145 "[PASSED]!!"
3146 % (
3147 dut,
3148 interface,
3149 interface_json["pktsIn"],
3150 interface_json["bytesIn"],
3151 )
3152 )
3153
3154 else:
3155 errormsg = (
3156 "[DUT %s]: Multicast traffic interface %s:"
3157 " Miss-match in "
3158 "PktsIn: %s, BytesIn: %s"
3159 "[FAILED]!!"
3160 % (
3161 dut,
3162 interface,
3163 interface_json["pktsIn"],
3164 interface_json["bytesIn"],
3165 )
3166 )
3167 return errormsg
3168
3169 if traffic_type == "traffic_sent":
3170 traffic_dict[traffic_type] = {}
3171 for interface in interfaces:
3172 traffic_dict[traffic_type][interface] = {}
3173 interface_json = show_multicast_traffic_json[interface]
3174
3175 if (
3176 interface_json["pktsOut"] == 0
3177 and interface_json["bytesOut"] == 0
3178 ):
3179 errormsg = (
3180 "[DUT %s]: Multicast traffic is "
3181 "not received on interface %s "
3182 "PktsIn: %s, BytesIn: %s"
3183 "[FAILED]!!"
3184 % (
3185 dut,
3186 interface,
3187 interface_json["pktsOut"],
3188 interface_json["bytesOut"],
3189 )
3190 )
3191 return errormsg
3192
3193 elif (
3194 interface_json["pktsOut"] != 0
3195 and interface_json["bytesOut"] != 0
3196 ):
3197
3198 traffic_dict[traffic_type][interface][
3199 "pktsOut"
3200 ] = interface_json["pktsOut"]
3201 traffic_dict[traffic_type][interface][
3202 "bytesOut"
3203 ] = interface_json["bytesOut"]
3204
3205 logger.info(
3206 "[DUT %s]: Multicast traffic is "
3207 "received on interface %s "
3208 "PktsOut: %s, BytesOut: %s "
3209 "[PASSED]!!"
3210 % (
3211 dut,
3212 interface,
3213 interface_json["pktsOut"],
3214 interface_json["bytesOut"],
3215 )
3216 )
3217 else:
3218 errormsg = (
3219 "[DUT %s]: Multicast traffic interface %s:"
3220 " Miss-match in "
3221 "PktsOut: %s, BytesOut: %s "
3222 "[FAILED]!!"
3223 % (
3224 dut,
3225 interface,
3226 interface_json["pktsOut"],
3227 interface_json["bytesOut"],
3228 )
3229 )
3230 return errormsg
3231
3232 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
3233 return True if return_traffic == False else traffic_dict
3234
3235
3236def get_refCount_for_mroute(tgen, dut, iif, src_address, group_addresses):
3237 """
3238 Verify upstream inbound interface is updated correctly
3239 by running "show ip pim upstream" cli
3240
3241 Parameters
3242 ----------
3243 * `tgen`: topogen object
3244 * `dut`: device under test
3245 * `iif`: inbound interface
3246 * `src_address`: source address
3247 * `group_addresses`: IGMP group address
3248
3249 Usage
3250 -----
3251 dut = "r1"
3252 iif = "r1-r0-eth0"
3253 src_address = "*"
3254 group_address = "225.1.1.1"
3255 result = get_refCount_for_mroute(tgen, dut, iif, src_address,
3256 group_address)
3257
3258 Returns
3259 -------
3260 refCount(int)
3261 """
3262
3263 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
3264
3265 refCount = 0
3266 if dut not in tgen.routers():
3267 return False
3268
3269 rnode = tgen.routers()[dut]
3270
3271 logger.info("[DUT: %s]: Verifying refCount for mroutes: ", dut)
3272 show_ip_pim_upstream_json = run_frr_cmd(
3273 rnode, "show ip pim upstream json", isjson=True
3274 )
3275
3276 if type(group_addresses) is not list:
3277 group_addresses = [group_addresses]
3278
3279 for grp_addr in group_addresses:
3280 # Verify group address
3281 if grp_addr not in show_ip_pim_upstream_json:
3282 errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % (
3283 dut,
3284 grp_addr,
3285 )
3286 return errormsg
3287 group_addr_json = show_ip_pim_upstream_json[grp_addr]
3288
3289 # Verify source address
3290 if src_address not in group_addr_json:
3291 errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % (
3292 dut,
3293 src_address,
3294 grp_addr,
3295 )
3296 return errormsg
3297
3298 # Verify Inbound Interface
3299 if group_addr_json[src_address]["inboundInterface"] == iif:
3300 refCount = group_addr_json[src_address]["refCount"]
3301
3302 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
3303 return refCount
3304
3305
ed776e38 3306@retry(retry_timeout=40)
a53c08bc
CH
3307def verify_multicast_flag_state(
3308 tgen, dut, src_address, group_addresses, flag, expected=True
3309):
e8cd26fd 3310 """
3311 Verify flag state for mroutes and make sure (*, G)/(S, G) are having
3312 coorect flags by running "show ip mroute" cli
3313
3314 Parameters
3315 ----------
3316 * `tgen`: topogen object
3317 * `dut`: device under test
3318 * `src_address`: source address
3319 * `group_addresses`: IGMP group address
3320 * `flag`: flag state, needs to be verified
3c41ebf8 3321 * `expected` : expected results from API, by-default True
e8cd26fd 3322
3323 Usage
3324 -----
3325 dut = "r1"
3326 flag = "SC"
3327 group_address = "225.1.1.1"
3328 result = verify_multicast_flag_state(tgen, dut, src_address,
3329 group_address, flag)
3330
3331 Returns
3332 -------
3333 errormsg(str) or True
3334 """
3335
3336 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
3337
3338 if dut not in tgen.routers():
3339 return False
3340
3341 rnode = tgen.routers()[dut]
3342
3343 logger.info("[DUT: %s]: Verifying flag state for mroutes", dut)
3344 show_ip_mroute_json = run_frr_cmd(rnode, "show ip mroute json", isjson=True)
3345
3346 if bool(show_ip_mroute_json) == False:
3347 error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut)
3348 return error_msg
3349
3350 if type(group_addresses) is not list:
3351 group_addresses = [group_addresses]
3352
3353 for grp_addr in group_addresses:
3354 if grp_addr not in show_ip_mroute_json:
3355 errormsg = (
3356 "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! ",
3357 dut,
3358 src_address,
3359 grp_addr,
3360 )
3361 return errormsg
3362 else:
3363 group_addr_json = show_ip_mroute_json[grp_addr]
3364
3365 if src_address not in group_addr_json:
3366 errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
3367 dut,
3368 src_address,
3369 grp_addr,
3370 )
3371 return errormsg
3372 else:
3373 mroutes = group_addr_json[src_address]
3374
3375 if mroutes["installed"] != 0:
3376 logger.info(
3377 "[DUT %s]: mroute (%s,%s) is installed", dut, src_address, grp_addr
3378 )
3379
3380 if mroutes["flags"] != flag:
3381 errormsg = (
3382 "[DUT %s]: Verifying flag for (%s, %s) "
3383 "mroute [FAILED]!! "
3384 "Expected: %s Found: %s"
3385 % (dut, src_address, grp_addr, flag, mroutes["flags"])
3386 )
3387 return errormsg
3388
3389 logger.info(
3390 "[DUT %s]: Verifying flag for (%s, %s)"
3391 " mroute, [PASSED]!! "
3392 "Found Expected: %s",
3393 dut,
3394 src_address,
3395 grp_addr,
3396 mroutes["flags"],
3397 )
3398
3399 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
3400 return True
eab72dc8 3401
3402
ed776e38 3403@retry(retry_timeout=40)
3c41ebf8 3404def verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip, expected=True):
eab72dc8 3405 """
3406 Verify all IGMP interface are up and running, config is verified
3407 using "show ip igmp interface" cli
3408
3409 Parameters
3410 ----------
3411 * `tgen`: topogen object
3412 * `topo` : json file data
3413 * `dut` : device under test
3414 * `igmp_iface` : interface name
3415 * `interface_ip` : interface ip address
3c41ebf8 3416 * `expected` : expected results from API, by-default True
eab72dc8 3417
3418 Usage
3419 -----
3420 result = verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip)
3421
3422 Returns
3423 -------
3424 errormsg(str) or True
3425 """
3426
3427 logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
3428
3429 for router in tgen.routers():
3430 if router != dut:
3431 continue
3432
5980ad0a 3433 logger.info("[DUT: %s]: Verifying PIM interface status:", dut)
eab72dc8 3434
3435 rnode = tgen.routers()[dut]
5980ad0a
DS
3436 show_ip_igmp_interface_json = run_frr_cmd(
3437 rnode, "show ip igmp interface json", isjson=True
3438 )
eab72dc8 3439
5980ad0a 3440 if igmp_iface in show_ip_igmp_interface_json:
eab72dc8 3441 igmp_intf_json = show_ip_igmp_interface_json[igmp_iface]
3442 # Verifying igmp interface
5980ad0a
DS
3443 if igmp_intf_json["address"] != interface_ip:
3444 errormsg = (
3445 "[DUT %s]: igmp interface ip is not correct "
3446 "[FAILED]!! Expected : %s, Found : %s"
3447 % (dut, igmp_intf_json["address"], interface_ip)
3448 )
eab72dc8 3449 return errormsg
3450
5980ad0a
DS
3451 logger.info(
3452 "[DUT %s]: igmp interface: %s, " "interface ip: %s" " [PASSED]!!",
3453 dut,
3454 igmp_iface,
3455 interface_ip,
3456 )
eab72dc8 3457 else:
5980ad0a
DS
3458 errormsg = (
3459 "[DUT %s]: igmp interface: %s "
3460 "igmp interface ip: %s, is not present "
3461 % (dut, igmp_iface, interface_ip)
3462 )
eab72dc8 3463 return errormsg
3464
3465 logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
3466 return True
a5124c49
CH
3467
3468
a53c08bc 3469class McastTesterHelper(HostApplicationHelper):
a5124c49
CH
3470 def __init__(self, tgen=None):
3471 self.script_path = os.path.join(CWD, "mcast-tester.py")
3472 self.host_conn = {}
3473 self.listen_sock = None
3474
3475 # # Get a temporary file for socket path
3476 # (fd, sock_path) = tempfile.mkstemp("-mct.sock", "tmp" + str(os.getpid()))
3477 # os.close(fd)
3478 # os.remove(sock_path)
3479 # self.app_sock_path = sock_path
3480
3481 # # Listen on unix socket
3482 # logger.debug("%s: listening on socket %s", self, self.app_sock_path)
3483 # self.listen_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
3484 # self.listen_sock.settimeout(10)
3485 # self.listen_sock.bind(self.app_sock_path)
3486 # self.listen_sock.listen(10)
3487
3488 python3_path = get_exec_path(["python3", "python"])
3489 super(McastTesterHelper, self).__init__(
3490 tgen,
3491 # [python3_path, self.script_path, self.app_sock_path]
a53c08bc 3492 [python3_path, self.script_path],
a5124c49
CH
3493 )
3494
a53c08bc 3495 def __str__(self):
a5124c49
CH
3496 return "McastTesterHelper({})".format(self.script_path)
3497
3498 def run_join(self, host, join_addrs, join_towards=None, join_intf=None):
3499 """
3500 Join a UDP multicast group.
3501
3502 One of join_towards or join_intf MUST be set.
3503
3504 Parameters:
3505 -----------
3506 * `host`: host from where IGMP join would be sent
3507 * `join_addrs`: multicast address (or addresses) to join to
3508 * `join_intf`: the interface to bind the join[s] to
3509 * `join_towards`: router whos interface to bind the join[s] to
3510 """
3511 if not isinstance(join_addrs, list) and not isinstance(join_addrs, tuple):
3512 join_addrs = [join_addrs]
3513
3514 if join_towards:
a53c08bc
CH
3515 join_intf = frr_unicode(
3516 self.tgen.json_topo["routers"][host]["links"][join_towards]["interface"]
3517 )
a5124c49
CH
3518 else:
3519 assert join_intf
3520
3521 for join in join_addrs:
3522 self.run(host, [join, join_intf])
3523
3524 return True
3525
3526 def run_traffic(self, host, send_to_addrs, bind_towards=None, bind_intf=None):
3527 """
3528 Send UDP multicast traffic.
3529
3530 One of bind_towards or bind_intf MUST be set.
3531
3532 Parameters:
3533 -----------
3534 * `host`: host to send traffic from
3535 * `send_to_addrs`: multicast address (or addresses) to send traffic to
3536 * `bind_towards`: Router who's interface the source ip address is got from
3537 """
3538 if bind_towards:
a53c08bc
CH
3539 bind_intf = frr_unicode(
3540 self.tgen.json_topo["routers"][host]["links"][bind_towards]["interface"]
3541 )
a5124c49
CH
3542 else:
3543 assert bind_intf
3544
3545 if not isinstance(send_to_addrs, list) and not isinstance(send_to_addrs, tuple):
3546 send_to_addrs = [send_to_addrs]
3547
3548 for send_to in send_to_addrs:
3549 self.run(host, ["--send=0.7", send_to, bind_intf])
3550
3551 return True
3552
3553 # def cleanup(self):
3554 # super(McastTesterHelper, self).cleanup()
3555
3556 # if not self.listen_sock:
3557 # return
3558
3559 # logger.debug("%s: closing listen socket %s", self, self.app_sock_path)
3560 # self.listen_sock.close()
3561 # self.listen_sock = None
3562
3563 # if os.path.exists(self.app_sock_path):
3564 # os.remove(self.app_sock_path)
3565
3566 # def started_proc(self, host, p):
3567 # logger.debug("%s: %s: accepting on socket %s", self, host, self.app_sock_path)
3568 # try:
3569 # conn = self.listen_sock.accept()
3570 # return conn
3571 # except Exception as error:
3572 # logger.error("%s: %s: accept on socket failed: %s", self, host, error)
3573 # if p.poll() is not None:
3574 # logger.error("%s: %s: helper app quit: %s", self, host, comm_error(p))
3575 # raise
3576
3577 # def stopping_proc(self, host, p, conn):
3578 # logger.debug("%s: %s: closing socket %s", self, host, conn)
3579 # conn[0].close()