mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-15 18:41:36 +00:00
Merge pull request #11164 from kuldeepkash/pim_v6
This commit is contained in:
commit
8d4abfc9b9
@ -449,6 +449,8 @@ def check_router_status(tgen):
|
||||
daemons.append("zebra")
|
||||
if "pimd" in result:
|
||||
daemons.append("pimd")
|
||||
if "pim6d" in result:
|
||||
daemons.append("pim6d")
|
||||
if "ospfd" in result:
|
||||
daemons.append("ospfd")
|
||||
if "ospf6d" in result:
|
||||
@ -1035,6 +1037,12 @@ def start_topology(tgen, daemon=None):
|
||||
TopoRouter.RD_PIM, "{}/{}/pimd.conf".format(tgen.logdir, rname)
|
||||
)
|
||||
|
||||
if daemon and "pim6d" in daemon:
|
||||
# Loading empty pimd.conf file to router, to start the pim6d deamon
|
||||
router.load_config(
|
||||
TopoRouter.RD_PIM6, "{}/{}/pim6d.conf".format(tgen.logdir, rname)
|
||||
)
|
||||
|
||||
# Starting routers
|
||||
logger.info("Starting all routers once topology is created")
|
||||
tgen.start_router()
|
||||
@ -1131,6 +1139,8 @@ def topo_daemons(tgen, topo=None):
|
||||
for val in topo["routers"][rtr]["links"].values():
|
||||
if "pim" in val and "pimd" not in daemon_list:
|
||||
daemon_list.append("pimd")
|
||||
if "pim6" in val and "pim6d" not in daemon_list:
|
||||
daemon_list.append("pim6d")
|
||||
if "ospf" in val and "ospfd" not in daemon_list:
|
||||
daemon_list.append("ospfd")
|
||||
if "ospf6" in val and "ospf6d" not in daemon_list:
|
||||
@ -3234,6 +3244,86 @@ def configure_interface_mac(tgen, input_dict):
|
||||
return True
|
||||
|
||||
|
||||
def socat_send_igmp_join_traffic(
|
||||
tgen,
|
||||
server,
|
||||
protocol_option,
|
||||
igmp_groups,
|
||||
send_from_intf,
|
||||
send_from_intf_ip=None,
|
||||
port=12345,
|
||||
reuseaddr=True,
|
||||
join=False,
|
||||
traffic=False,
|
||||
):
|
||||
"""
|
||||
API to send IGMP join using SOCAT tool
|
||||
|
||||
Parameters:
|
||||
-----------
|
||||
* `tgen` : Topogen object
|
||||
* `server`: iperf server, from where IGMP join would be sent
|
||||
* `protocol_option`: Protocol options, ex: UDP6-RECV
|
||||
* `igmp_groups`: IGMP group for which join has to be sent
|
||||
* `send_from_intf`: Interface from which join would be sent
|
||||
* `send_from_intf_ip`: Interface IP, default is None
|
||||
* `port`: Port to be used, default is 12345
|
||||
* `reuseaddr`: True|False, bydefault True
|
||||
* `join`: If join needs to be sent
|
||||
* `traffic`: If traffic needs to be sent
|
||||
|
||||
returns:
|
||||
--------
|
||||
errormsg or True
|
||||
"""
|
||||
|
||||
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
|
||||
|
||||
rnode = tgen.routers()[server]
|
||||
socat_cmd = "socat -u "
|
||||
|
||||
# UDP4/TCP4/UDP6/UDP6-RECV
|
||||
if protocol_option:
|
||||
socat_cmd += "{}".format(protocol_option)
|
||||
|
||||
if port:
|
||||
socat_cmd += ":{},".format(port)
|
||||
|
||||
if reuseaddr:
|
||||
socat_cmd += "{},".format("reuseaddr")
|
||||
|
||||
# Group address range to cover
|
||||
if igmp_groups:
|
||||
if not isinstance(igmp_groups, list):
|
||||
igmp_groups = [igmp_groups]
|
||||
|
||||
for igmp_group in igmp_groups:
|
||||
if join:
|
||||
join_traffic_option = "ipv6-join-group"
|
||||
elif traffic:
|
||||
join_traffic_option = "ipv6-join-group-source"
|
||||
|
||||
if send_from_intf and not send_from_intf_ip:
|
||||
socat_cmd += "{}='[{}]:{}'".format(
|
||||
join_traffic_option, igmp_group, send_from_intf
|
||||
)
|
||||
else:
|
||||
socat_cmd += "{}='[{}]:{}:[{}]'".format(
|
||||
join_traffic_option, igmp_group, send_from_intf, send_from_intf_ip
|
||||
)
|
||||
|
||||
socat_cmd += " STDOUT"
|
||||
|
||||
socat_cmd += " &>{}/socat.logs &".format(tgen.logdir)
|
||||
|
||||
# Run socat command to send IGMP join
|
||||
logger.info("[DUT: {}]: Running command: [{}]".format(server, socat_cmd))
|
||||
output = rnode.run("set +m; {} sleep 0.5".format(socat_cmd))
|
||||
|
||||
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
|
||||
return True
|
||||
|
||||
|
||||
#############################################
|
||||
# Verification APIs
|
||||
#############################################
|
||||
|
@ -36,6 +36,7 @@ from lib.common_config import (
|
||||
InvalidCLIError,
|
||||
retry,
|
||||
run_frr_cmd,
|
||||
validate_ip_address,
|
||||
)
|
||||
from lib.micronet import get_exec_path
|
||||
from lib.topolog import logger
|
||||
@ -47,7 +48,7 @@ CWD = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True):
|
||||
"""
|
||||
API to configure pim on router
|
||||
API to configure pim/pimv6 on router
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@ -70,6 +71,16 @@ def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True
|
||||
"prefix-list": "pf_list_1"
|
||||
"delete": True
|
||||
}]
|
||||
},
|
||||
"pim6": {
|
||||
"disable" : ["l1-i1-eth1"],
|
||||
"rp": [{
|
||||
"rp_addr" : "2001:db8:f::5:17".
|
||||
"keep-alive-timer": "100"
|
||||
"group_addr_range": ["FF00::/8"]
|
||||
"prefix-list": "pf_list_1"
|
||||
"delete": True
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -97,12 +108,8 @@ def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True
|
||||
|
||||
# Now add RP config to all routers
|
||||
for router in input_dict.keys():
|
||||
if "pim" not in input_dict[router]:
|
||||
continue
|
||||
if "rp" not in input_dict[router]["pim"]:
|
||||
continue
|
||||
_add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict)
|
||||
|
||||
if "pim" in input_dict[router] or "pim6" in input_dict[router]:
|
||||
_add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict)
|
||||
try:
|
||||
result = create_common_configurations(
|
||||
tgen, config_data_dict, "pim", build, load_config
|
||||
@ -133,81 +140,123 @@ def _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict):
|
||||
"""
|
||||
|
||||
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
|
||||
rp_data = []
|
||||
|
||||
pim_data = input_dict[router]["pim"]
|
||||
rp_data = pim_data["rp"]
|
||||
# PIMv4
|
||||
pim_data = None
|
||||
if "pim" in input_dict[router]:
|
||||
pim_data = input_dict[router]["pim"]
|
||||
if "rp" in input_dict[router]["pim"]:
|
||||
rp_data += pim_data["rp"]
|
||||
|
||||
# PIMv6
|
||||
pim6_data = None
|
||||
if "pim6" in input_dict[router]:
|
||||
pim6_data = input_dict[router]["pim6"]
|
||||
if "rp" in input_dict[router]["pim6"]:
|
||||
rp_data += pim6_data["rp"]
|
||||
|
||||
# Configure this RP on every router.
|
||||
for dut in tgen.routers():
|
||||
# At least one interface must be enabled for PIM on the router
|
||||
pim_if_enabled = False
|
||||
pim6_if_enabled = False
|
||||
for destLink, data in topo[dut]["links"].items():
|
||||
if "pim" in data:
|
||||
pim_if_enabled = True
|
||||
if not pim_if_enabled:
|
||||
if "pim6" in data:
|
||||
pim6_if_enabled = True
|
||||
if not pim_if_enabled and pim_data:
|
||||
continue
|
||||
if not pim6_if_enabled and pim6_data:
|
||||
continue
|
||||
|
||||
config_data = []
|
||||
|
||||
for rp_dict in deepcopy(rp_data):
|
||||
# ip address of RP
|
||||
if "rp_addr" not in rp_dict and build:
|
||||
logger.error(
|
||||
"Router %s: 'ip address of RP' not " "present in input_dict/JSON",
|
||||
router,
|
||||
)
|
||||
if rp_data:
|
||||
for rp_dict in deepcopy(rp_data):
|
||||
# ip address of RP
|
||||
if "rp_addr" not in rp_dict and build:
|
||||
logger.error(
|
||||
"Router %s: 'ip address of RP' not "
|
||||
"present in input_dict/JSON",
|
||||
router,
|
||||
)
|
||||
|
||||
return False
|
||||
rp_addr = rp_dict.setdefault("rp_addr", None)
|
||||
return False
|
||||
rp_addr = rp_dict.setdefault("rp_addr", None)
|
||||
if rp_addr:
|
||||
addr_type = validate_ip_address(rp_addr)
|
||||
# Keep alive Timer
|
||||
keep_alive_timer = rp_dict.setdefault("keep_alive_timer", None)
|
||||
|
||||
# Keep alive Timer
|
||||
keep_alive_timer = rp_dict.setdefault("keep_alive_timer", None)
|
||||
# Group Address range to cover
|
||||
if "group_addr_range" not in rp_dict and build:
|
||||
logger.error(
|
||||
"Router %s:'Group Address range to cover'"
|
||||
" not present in input_dict/JSON",
|
||||
router,
|
||||
)
|
||||
|
||||
# Group Address range to cover
|
||||
if "group_addr_range" not in rp_dict and build:
|
||||
logger.error(
|
||||
"Router %s:'Group Address range to cover'"
|
||||
" not present in input_dict/JSON",
|
||||
router,
|
||||
)
|
||||
return False
|
||||
group_addr_range = rp_dict.setdefault("group_addr_range", None)
|
||||
|
||||
return False
|
||||
group_addr_range = rp_dict.setdefault("group_addr_range", None)
|
||||
# Group prefix-list filter
|
||||
prefix_list = rp_dict.setdefault("prefix_list", None)
|
||||
|
||||
# Group prefix-list filter
|
||||
prefix_list = rp_dict.setdefault("prefix_list", None)
|
||||
# Delete rp config
|
||||
del_action = rp_dict.setdefault("delete", False)
|
||||
|
||||
# Delete rp config
|
||||
del_action = rp_dict.setdefault("delete", False)
|
||||
|
||||
if keep_alive_timer:
|
||||
cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer)
|
||||
if del_action:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
|
||||
if rp_addr:
|
||||
if group_addr_range:
|
||||
if type(group_addr_range) is not list:
|
||||
group_addr_range = [group_addr_range]
|
||||
|
||||
for grp_addr in group_addr_range:
|
||||
cmd = "ip pim rp {} {}".format(rp_addr, grp_addr)
|
||||
if keep_alive_timer:
|
||||
if addr_type == "ipv4":
|
||||
cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer)
|
||||
if del_action:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
if addr_type == "ipv6":
|
||||
cmd = "ipv6 pim rp keep-alive-timer {}".format(keep_alive_timer)
|
||||
if del_action:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
|
||||
if prefix_list:
|
||||
cmd = "ip pim rp {} prefix-list {}".format(rp_addr, prefix_list)
|
||||
if del_action:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
if rp_addr:
|
||||
if group_addr_range:
|
||||
if type(group_addr_range) is not list:
|
||||
group_addr_range = [group_addr_range]
|
||||
|
||||
if config_data:
|
||||
if dut not in config_data_dict:
|
||||
config_data_dict[dut] = config_data
|
||||
else:
|
||||
config_data_dict[dut].extend(config_data)
|
||||
for grp_addr in group_addr_range:
|
||||
if addr_type == "ipv4":
|
||||
cmd = "ip pim rp {} {}".format(rp_addr, grp_addr)
|
||||
if del_action:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
if addr_type == "ipv6":
|
||||
cmd = "ipv6 pim rp {} {}".format(rp_addr, grp_addr)
|
||||
if del_action:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
|
||||
if prefix_list:
|
||||
if addr_type == "ipv4":
|
||||
cmd = "ip pim rp {} prefix-list {}".format(
|
||||
rp_addr, prefix_list
|
||||
)
|
||||
if del_action:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
if addr_type == "ipv6":
|
||||
cmd = "ipv6 pim rp {} prefix-list {}".format(
|
||||
rp_addr, prefix_list
|
||||
)
|
||||
if del_action:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
|
||||
if config_data:
|
||||
if dut not in config_data_dict:
|
||||
config_data_dict[dut] = config_data
|
||||
else:
|
||||
config_data_dict[dut].extend(config_data)
|
||||
|
||||
|
||||
def create_igmp_config(tgen, topo, input_dict=None, build=False):
|
||||
@ -319,6 +368,121 @@ def create_igmp_config(tgen, topo, input_dict=None, build=False):
|
||||
return result
|
||||
|
||||
|
||||
def create_mld_config(tgen, topo, input_dict=None, build=False):
|
||||
"""
|
||||
API to configure mld for PIMv6 on router
|
||||
|
||||
Parameters
|
||||
----------
|
||||
* `tgen` : Topogen object
|
||||
* `topo` : json file data
|
||||
* `input_dict` : Input dict data, required when configuring from
|
||||
testcase
|
||||
* `build` : Only for initial setup phase this is set as True.
|
||||
|
||||
Usage
|
||||
-----
|
||||
input_dict = {
|
||||
"r1": {
|
||||
"mld": {
|
||||
"interfaces": {
|
||||
"r1-r0-eth0" :{
|
||||
"mld":{
|
||||
"version": "2",
|
||||
"delete": True
|
||||
"query": {
|
||||
"query-interval" : 100,
|
||||
"query-max-response-time": 200
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Returns
|
||||
-------
|
||||
True or False
|
||||
"""
|
||||
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
|
||||
result = False
|
||||
if not input_dict:
|
||||
input_dict = deepcopy(topo)
|
||||
else:
|
||||
topo = topo["routers"]
|
||||
input_dict = deepcopy(input_dict)
|
||||
for router in input_dict.keys():
|
||||
if "mld" not in input_dict[router]:
|
||||
logger.debug("Router %s: 'mld' is not present in " "input_dict", router)
|
||||
continue
|
||||
|
||||
mld_data = input_dict[router]["mld"]
|
||||
|
||||
if "interfaces" in mld_data:
|
||||
config_data = []
|
||||
intf_data = mld_data["interfaces"]
|
||||
|
||||
for intf_name in intf_data.keys():
|
||||
cmd = "interface {}".format(intf_name)
|
||||
config_data.append(cmd)
|
||||
protocol = "mld"
|
||||
del_action = intf_data[intf_name]["mld"].setdefault("delete", False)
|
||||
cmd = "ipv6 mld"
|
||||
if del_action:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
|
||||
del_attr = intf_data[intf_name]["mld"].setdefault("delete_attr", False)
|
||||
join = intf_data[intf_name]["mld"].setdefault("join", None)
|
||||
source = intf_data[intf_name]["mld"].setdefault("source", None)
|
||||
version = intf_data[intf_name]["mld"].setdefault("version", False)
|
||||
query = intf_data[intf_name]["mld"].setdefault("query", {})
|
||||
|
||||
if version:
|
||||
cmd = "ipv6 {} version {}".format(protocol, version)
|
||||
if del_action:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
|
||||
if source and join:
|
||||
for group in join:
|
||||
cmd = "ipv6 {} join {} {}".format(protocol, group, source)
|
||||
|
||||
if del_attr:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
|
||||
elif join:
|
||||
for group in join:
|
||||
cmd = "ipv6 {} join {}".format(protocol, group)
|
||||
|
||||
if del_attr:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
|
||||
if query:
|
||||
for _query, value in query.items():
|
||||
if _query != "delete":
|
||||
cmd = "ipv6 {} {} {}".format(protocol, _query, value)
|
||||
|
||||
if "delete" in intf_data[intf_name][protocol]["query"]:
|
||||
cmd = "no {}".format(cmd)
|
||||
|
||||
config_data.append(cmd)
|
||||
try:
|
||||
result = create_common_configuration(
|
||||
tgen, router, config_data, "interface_config", build=build
|
||||
)
|
||||
except InvalidCLIError:
|
||||
errormsg = traceback.format_exc()
|
||||
logger.error(errormsg)
|
||||
return errormsg
|
||||
|
||||
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
|
||||
return result
|
||||
|
||||
|
||||
def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False):
|
||||
"""
|
||||
Helper API to enable or disable pim on interfaces
|
||||
@ -338,7 +502,7 @@ def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False):
|
||||
|
||||
config_data = []
|
||||
|
||||
# Enable pim on interfaces
|
||||
# Enable pim/pim6 on interfaces
|
||||
for destRouterLink, data in sorted(topo[router]["links"].items()):
|
||||
if "pim" in data and data["pim"] == "enable":
|
||||
# Loopback interfaces
|
||||
@ -351,6 +515,17 @@ def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False):
|
||||
config_data.append(cmd)
|
||||
config_data.append("ip pim")
|
||||
|
||||
if "pim6" in data and data["pim6"] == "enable":
|
||||
# Loopback interfaces
|
||||
if "type" in data and data["type"] == "loopback":
|
||||
interface_name = destRouterLink
|
||||
else:
|
||||
interface_name = data["interface"]
|
||||
|
||||
cmd = "interface {}".format(interface_name)
|
||||
config_data.append(cmd)
|
||||
config_data.append("ipv6 pim")
|
||||
|
||||
# pim global config
|
||||
if "pim" in input_dict[router]:
|
||||
pim_data = input_dict[router]["pim"]
|
||||
@ -366,6 +541,21 @@ def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False):
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
|
||||
# pim6 global config
|
||||
if "pim6" in input_dict[router]:
|
||||
pim6_data = input_dict[router]["pim6"]
|
||||
del_action = pim6_data.setdefault("delete", False)
|
||||
for t in [
|
||||
"join-prune-interval",
|
||||
"keep-alive-timer",
|
||||
"register-suppress-time",
|
||||
]:
|
||||
if t in pim6_data:
|
||||
cmd = "ipv6 pim {} {}".format(t, pim6_data[t])
|
||||
if del_action:
|
||||
cmd = "no {}".format(cmd)
|
||||
config_data.append(cmd)
|
||||
|
||||
return config_data
|
||||
|
||||
|
||||
@ -732,9 +922,6 @@ def verify_upstream_iif(
|
||||
"[DUT: %s]: Verifying upstream Inbound Interface" " for IGMP groups received:",
|
||||
dut,
|
||||
)
|
||||
show_ip_pim_upstream_json = run_frr_cmd(
|
||||
rnode, "show ip pim upstream json", isjson=True
|
||||
)
|
||||
|
||||
if type(group_addresses) is not list:
|
||||
group_addresses = [group_addresses]
|
||||
@ -742,6 +929,17 @@ def verify_upstream_iif(
|
||||
if type(iif) is not list:
|
||||
iif = [iif]
|
||||
|
||||
for grp in group_addresses:
|
||||
addr_type = validate_ip_address(grp)
|
||||
|
||||
if addr_type == "ipv4":
|
||||
ip_cmd = "ip"
|
||||
elif addr_type == "ipv6":
|
||||
ip_cmd = "ipv6"
|
||||
|
||||
cmd = "show {} pim upstream json".format(ip_cmd)
|
||||
show_ip_pim_upstream_json = run_frr_cmd(rnode, cmd, isjson=True)
|
||||
|
||||
for grp_addr in group_addresses:
|
||||
# Verify group address
|
||||
if grp_addr not in show_ip_pim_upstream_json:
|
||||
@ -883,13 +1081,19 @@ def verify_join_state_and_timer(
|
||||
"[DUT: %s]: Verifying Join state and Join Timer" " for IGMP groups received:",
|
||||
dut,
|
||||
)
|
||||
show_ip_pim_upstream_json = run_frr_cmd(
|
||||
rnode, "show ip pim upstream json", isjson=True
|
||||
)
|
||||
|
||||
if type(group_addresses) is not list:
|
||||
group_addresses = [group_addresses]
|
||||
|
||||
for grp in group_addresses:
|
||||
addr_type = validate_ip_address(grp)
|
||||
|
||||
if addr_type == "ipv4":
|
||||
cmd = "show ip pim upstream json"
|
||||
elif addr_type == "ipv6":
|
||||
cmd = "show ipv6 pim upstream json"
|
||||
show_ip_pim_upstream_json = run_frr_cmd(rnode, cmd, isjson=True)
|
||||
|
||||
for grp_addr in group_addresses:
|
||||
# Verify group address
|
||||
if grp_addr not in show_ip_pim_upstream_json:
|
||||
@ -1010,20 +1214,6 @@ def verify_mroutes(
|
||||
|
||||
rnode = tgen.routers()[dut]
|
||||
|
||||
if return_uptime:
|
||||
logger.info("Sleeping for %s sec..", mwait)
|
||||
sleep(mwait)
|
||||
|
||||
logger.info("[DUT: %s]: Verifying ip mroutes", dut)
|
||||
show_ip_mroute_json = run_frr_cmd(rnode, "show ip mroute json", isjson=True)
|
||||
|
||||
if return_uptime:
|
||||
uptime_dict = {}
|
||||
|
||||
if bool(show_ip_mroute_json) == False:
|
||||
error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut)
|
||||
return error_msg
|
||||
|
||||
if not isinstance(group_addresses, list):
|
||||
group_addresses = [group_addresses]
|
||||
|
||||
@ -1033,6 +1223,30 @@ def verify_mroutes(
|
||||
if not isinstance(oil, list) and oil != "none":
|
||||
oil = [oil]
|
||||
|
||||
for grp in group_addresses:
|
||||
addr_type = validate_ip_address(grp)
|
||||
|
||||
if addr_type == "ipv4":
|
||||
ip_cmd = "ip"
|
||||
elif addr_type == "ipv6":
|
||||
ip_cmd = "ipv6"
|
||||
|
||||
if return_uptime:
|
||||
logger.info("Sleeping for %s sec..", mwait)
|
||||
sleep(mwait)
|
||||
|
||||
logger.info("[DUT: %s]: Verifying ip mroutes", dut)
|
||||
show_ip_mroute_json = run_frr_cmd(
|
||||
rnode, "show {} mroute json".format(ip_cmd), isjson=True
|
||||
)
|
||||
|
||||
if return_uptime:
|
||||
uptime_dict = {}
|
||||
|
||||
if bool(show_ip_mroute_json) == False:
|
||||
error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut)
|
||||
return error_msg
|
||||
|
||||
for grp_addr in group_addresses:
|
||||
if grp_addr not in show_ip_mroute_json:
|
||||
errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
|
||||
@ -1214,15 +1428,20 @@ def verify_pim_rp_info(
|
||||
|
||||
rnode = tgen.routers()[dut]
|
||||
|
||||
logger.info("[DUT: %s]: Verifying ip rp info", dut)
|
||||
show_ip_rp_info_json = run_frr_cmd(rnode, "show ip pim rp-info json", isjson=True)
|
||||
|
||||
if type(group_addresses) is not list:
|
||||
group_addresses = [group_addresses]
|
||||
|
||||
if type(oif) is not list:
|
||||
oif = [oif]
|
||||
|
||||
for grp in group_addresses:
|
||||
addr_type = validate_ip_address(grp)
|
||||
|
||||
if addr_type == "ipv4":
|
||||
ip_cmd = "ip"
|
||||
elif addr_type == "ipv6":
|
||||
ip_cmd = "ipv6"
|
||||
|
||||
for grp_addr in group_addresses:
|
||||
if rp is None:
|
||||
rp_details = find_rp_details(tgen, topo)
|
||||
@ -1232,9 +1451,14 @@ def verify_pim_rp_info(
|
||||
else:
|
||||
iamRP = False
|
||||
else:
|
||||
show_ip_route_json = run_frr_cmd(
|
||||
rnode, "show ip route connected json", isjson=True
|
||||
)
|
||||
if addr_type == "ipv4":
|
||||
show_ip_route_json = run_frr_cmd(
|
||||
rnode, "show ip route connected json", isjson=True
|
||||
)
|
||||
elif addr_type == "ipv6":
|
||||
show_ip_route_json = run_frr_cmd(
|
||||
rnode, "show ipv6 route connected json", isjson=True
|
||||
)
|
||||
for _rp in show_ip_route_json.keys():
|
||||
if rp == _rp.split("/")[0]:
|
||||
iamRP = True
|
||||
@ -1242,16 +1466,27 @@ def verify_pim_rp_info(
|
||||
else:
|
||||
iamRP = False
|
||||
|
||||
logger.info("[DUT: %s]: Verifying ip rp info", dut)
|
||||
cmd = "show {} pim rp-info json".format(ip_cmd)
|
||||
show_ip_rp_info_json = run_frr_cmd(rnode, cmd, isjson=True)
|
||||
|
||||
if rp not in show_ip_rp_info_json:
|
||||
errormsg = "[DUT %s]: Verifying rp-info" "for rp_address %s [FAILED]!! " % (
|
||||
dut,
|
||||
rp,
|
||||
errormsg = (
|
||||
"[DUT %s]: Verifying rp-info "
|
||||
"for rp_address %s [FAILED]!! " % (dut, rp)
|
||||
)
|
||||
return errormsg
|
||||
else:
|
||||
group_addr_json = show_ip_rp_info_json[rp]
|
||||
|
||||
for rp_json in group_addr_json:
|
||||
if "rpAddress" not in rp_json:
|
||||
errormsg = "[DUT %s]: %s key not " "present in rp-info " % (
|
||||
dut,
|
||||
"rpAddress",
|
||||
)
|
||||
return errormsg
|
||||
|
||||
if oif is not None:
|
||||
found = False
|
||||
if rp_json["outboundInterface"] not in oif:
|
||||
@ -1380,14 +1615,26 @@ def verify_pim_state(
|
||||
rnode = tgen.routers()[dut]
|
||||
|
||||
logger.info("[DUT: %s]: Verifying pim state", dut)
|
||||
show_pim_state_json = run_frr_cmd(rnode, "show ip pim state json", isjson=True)
|
||||
|
||||
if installed_fl is None:
|
||||
installed_fl = 1
|
||||
|
||||
if type(group_addresses) is not list:
|
||||
group_addresses = [group_addresses]
|
||||
|
||||
for grp in group_addresses:
|
||||
addr_type = validate_ip_address(grp)
|
||||
|
||||
if addr_type == "ipv4":
|
||||
ip_cmd = "ip"
|
||||
elif addr_type == "ipv6":
|
||||
ip_cmd = "ipv6"
|
||||
|
||||
logger.info("[DUT: %s]: Verifying pim state", dut)
|
||||
show_pim_state_json = run_frr_cmd(
|
||||
rnode, "show {} pim state json".format(ip_cmd), isjson=True
|
||||
)
|
||||
|
||||
if installed_fl is None:
|
||||
installed_fl = 1
|
||||
|
||||
for grp_addr in group_addresses:
|
||||
if src_address is None:
|
||||
src_address = "*"
|
||||
@ -3635,7 +3882,7 @@ def verify_local_igmp_groups(tgen, dut, interface, group_addresses):
|
||||
return True
|
||||
|
||||
|
||||
def verify_pim_interface_traffic(tgen, input_dict, return_stats=True):
|
||||
def verify_pim_interface_traffic(tgen, input_dict, return_stats=True, addr_type="ipv4"):
|
||||
"""
|
||||
Verify ip pim interface traffice by running
|
||||
"show ip pim interface traffic" cli
|
||||
@ -3645,6 +3892,8 @@ def verify_pim_interface_traffic(tgen, input_dict, return_stats=True):
|
||||
* `tgen`: topogen object
|
||||
* `input_dict(dict)`: defines DUT, what and from which interfaces
|
||||
traffic needs to be verified
|
||||
* [optional]`addr_type`: specify address-family, default is ipv4
|
||||
|
||||
Usage
|
||||
-----
|
||||
input_dict = {
|
||||
@ -3675,9 +3924,13 @@ def verify_pim_interface_traffic(tgen, input_dict, return_stats=True):
|
||||
rnode = tgen.routers()[dut]
|
||||
|
||||
logger.info("[DUT: %s]: Verifying pim interface traffic", dut)
|
||||
show_pim_intf_traffic_json = run_frr_cmd(
|
||||
rnode, "show ip pim interface traffic json", isjson=True
|
||||
)
|
||||
|
||||
if addr_type == "ipv4":
|
||||
cmd = "show ip pim interface traffic json"
|
||||
elif addr_type == "ipv6":
|
||||
cmd = "show ipv6 pim interface traffic json"
|
||||
|
||||
show_pim_intf_traffic_json = run_frr_cmd(rnode, cmd, isjson=True)
|
||||
|
||||
output_dict[dut] = {}
|
||||
for intf, data in input_dict[dut].items():
|
||||
|
@ -725,6 +725,7 @@ class TopoRouter(TopoGear):
|
||||
RD_PBRD = 16
|
||||
RD_PATH = 17
|
||||
RD_SNMP = 18
|
||||
RD_PIM6 = 19
|
||||
RD = {
|
||||
RD_FRR: "frr",
|
||||
RD_ZEBRA: "zebra",
|
||||
@ -735,6 +736,7 @@ class TopoRouter(TopoGear):
|
||||
RD_ISIS: "isisd",
|
||||
RD_BGP: "bgpd",
|
||||
RD_PIM: "pimd",
|
||||
RD_PIM6: "pim6d",
|
||||
RD_LDP: "ldpd",
|
||||
RD_EIGRP: "eigrpd",
|
||||
RD_NHRP: "nhrpd",
|
||||
@ -820,7 +822,8 @@ class TopoRouter(TopoGear):
|
||||
Possible daemon values are: TopoRouter.RD_ZEBRA, TopoRouter.RD_RIP,
|
||||
TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6,
|
||||
TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP,
|
||||
TopoRouter.RD_PIM, TopoRouter.RD_PBR, TopoRouter.RD_SNMP.
|
||||
TopoRouter.RD_PIM, TopoRouter.RD_PIM6, TopoRouter.RD_PBR,
|
||||
TopoRouter.RD_SNMP.
|
||||
|
||||
Possible `source` values are `None` for an empty config file, a path name which is
|
||||
used directly, or a file name with no path components which is first looked for
|
||||
@ -1276,6 +1279,7 @@ def diagnose_env_linux(rundir):
|
||||
"ripngd",
|
||||
"isisd",
|
||||
"pimd",
|
||||
"pim6d",
|
||||
"ldpd",
|
||||
"pbrd",
|
||||
]:
|
||||
|
@ -41,7 +41,11 @@ from lib.common_config import (
|
||||
number_to_column,
|
||||
)
|
||||
from lib.ospf import create_router_ospf
|
||||
from lib.pim import create_igmp_config, create_pim_config
|
||||
from lib.pim import (
|
||||
create_igmp_config,
|
||||
create_pim_config,
|
||||
create_mld_config,
|
||||
)
|
||||
from lib.topolog import logger
|
||||
|
||||
|
||||
@ -332,6 +336,7 @@ def build_config_from_json(tgen, topo=None, save_bkup=True):
|
||||
("route_maps", create_route_maps),
|
||||
("pim", create_pim_config),
|
||||
("igmp", create_igmp_config),
|
||||
("mld", create_mld_config),
|
||||
("bgp", create_router_bgp),
|
||||
("ospf", create_router_ospf),
|
||||
]
|
||||
@ -352,7 +357,9 @@ def build_config_from_json(tgen, topo=None, save_bkup=True):
|
||||
logger.info("build_config_from_json: failed to configure topology")
|
||||
pytest.exit(1)
|
||||
|
||||
logger.info("Built config now clearing ospf neighbors as that router-id might not be what is used")
|
||||
logger.info(
|
||||
"Built config now clearing ospf neighbors as that router-id might not be what is used"
|
||||
)
|
||||
for ospf in ["ospf", "ospf6"]:
|
||||
for router in data:
|
||||
if ospf not in data[router]:
|
||||
|
@ -1330,6 +1330,7 @@ class Router(Node):
|
||||
"isisd": 0,
|
||||
"bgpd": 0,
|
||||
"pimd": 0,
|
||||
"pim6d": 0,
|
||||
"ldpd": 0,
|
||||
"eigrpd": 0,
|
||||
"nhrpd": 0,
|
||||
|
@ -0,0 +1,197 @@
|
||||
{
|
||||
"address_types": ["ipv6"],
|
||||
"ipv6base": "fd00::",
|
||||
"ipv6mask": 64,
|
||||
"link_ip_start": {
|
||||
"ipv6": "fd00::",
|
||||
"v6mask": 64
|
||||
},
|
||||
"lo_prefix": {
|
||||
"ipv6": "2001:db8:f::",
|
||||
"v6mask": 128
|
||||
},
|
||||
"routers": {
|
||||
"r0": {
|
||||
"links": {
|
||||
"r1": {"ipv6": "auto"}
|
||||
}
|
||||
},
|
||||
"r1": {
|
||||
"links": {
|
||||
"lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}},
|
||||
"r0": {"ipv6": "auto", "pim6": "enable"},
|
||||
"r2": {"ipv6": "auto", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}},
|
||||
"r3": {"ipv6": "auto", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}},
|
||||
"r4": {"ipv6": "auto", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}}
|
||||
},
|
||||
"ospf6": {
|
||||
"router_id": "100.1.1.0",
|
||||
"neighbors": {
|
||||
"r2": {},
|
||||
"r3": {},
|
||||
"r4": {}
|
||||
},
|
||||
"redistribute": [
|
||||
{
|
||||
"redist_type": "static"
|
||||
},
|
||||
{
|
||||
"redist_type": "connected"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mld": {
|
||||
"interfaces": {
|
||||
"r1-r0-eth0" :{
|
||||
"mld":{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"r2": {
|
||||
"links": {
|
||||
"lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}},
|
||||
"r1": {"ipv6": "auto", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}},
|
||||
"r3": {"ipv6": "auto", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}}
|
||||
},
|
||||
"ospf6": {
|
||||
"router_id": "100.1.1.1",
|
||||
"neighbors": {
|
||||
"r1": {},
|
||||
"r3": {}
|
||||
},
|
||||
"redistribute": [
|
||||
{
|
||||
"redist_type": "static"
|
||||
},
|
||||
{
|
||||
"redist_type": "connected"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"r3": {
|
||||
"links": {
|
||||
"lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}},
|
||||
"r1": {"ipv6": "auto", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}},
|
||||
"r2": {"ipv6": "auto", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}},
|
||||
"r4": {"ipv6": "auto", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}},
|
||||
"r5": {"ipv6": "auto", "pim6": "enable"}
|
||||
},
|
||||
"ospf6": {
|
||||
"router_id": "100.1.1.2",
|
||||
"neighbors": {
|
||||
"r1": {},
|
||||
"r2": {},
|
||||
"r4": {}
|
||||
},
|
||||
"redistribute": [
|
||||
{
|
||||
"redist_type": "static"
|
||||
},
|
||||
{
|
||||
"redist_type": "connected"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"r4": {
|
||||
"links": {
|
||||
"lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}},
|
||||
"r1": {"ipv6": "auto", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}},
|
||||
"r3": {"ipv6": "auto", "pim6": "enable",
|
||||
"ospf6": {
|
||||
"area": "0.0.0.0",
|
||||
"hello_interval": 1,
|
||||
"dead_interval": 4
|
||||
}}
|
||||
},
|
||||
"ospf6": {
|
||||
"router_id": "100.1.1.3",
|
||||
"neighbors": {
|
||||
"r1": {},
|
||||
"r3": {}
|
||||
},
|
||||
"redistribute": [
|
||||
{
|
||||
"redist_type": "static"
|
||||
},
|
||||
{
|
||||
"redist_type": "connected"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"r5": {
|
||||
"links": {
|
||||
"r3": {"ipv6": "auto"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
412
tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pimv6_static_rp.py
Executable file
412
tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pimv6_static_rp.py
Executable file
@ -0,0 +1,412 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# Copyright (c) 2022 by VMware, Inc. ("VMware")
|
||||
# Used Copyright (c) 2018 by Network Device Education Foundation,
|
||||
# Inc. ("NetDEF") in this file.
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software
|
||||
# for any purpose with or without fee is hereby granted, provided
|
||||
# that the above copyright notice and this permission notice appear
|
||||
# in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
|
||||
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
# OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
"""
|
||||
Following tests are covered to test Multicast basic functionality:
|
||||
|
||||
Topology:
|
||||
|
||||
_______r2_____
|
||||
| |
|
||||
iperf | | iperf
|
||||
r0-----r1-------------r3-----r5
|
||||
| |
|
||||
|_____________|
|
||||
r4
|
||||
|
||||
Test steps
|
||||
- Create topology (setup module)
|
||||
- Bring up topology
|
||||
|
||||
TC_1 : Verify upstream interfaces(IIF) and join state are updated properly
|
||||
after adding and deleting the static RP
|
||||
TC_2 : Verify IIF and OIL in "show ip pim state" updated properly after
|
||||
adding and deleting the static RP
|
||||
TC_3: (*, G) Mroute entry are cleared when static RP gets deleted
|
||||
TC_4: Verify (*,G) prune is send towards the RP after deleting the static RP
|
||||
TC_24 : Verify (*,G) and (S,G) populated correctly when SPT and RPT share the
|
||||
same path
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import pytest
|
||||
from time import sleep
|
||||
import datetime
|
||||
|
||||
# Save the Current Working Directory to find configuration files.
|
||||
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.append(os.path.join(CWD, "../"))
|
||||
sys.path.append(os.path.join(CWD, "../lib/"))
|
||||
|
||||
# Required to instantiate the topology builder class.
|
||||
|
||||
# pylint: disable=C0413
|
||||
# Import topogen and topotest helpers
|
||||
from lib.topogen import Topogen, get_topogen
|
||||
|
||||
from lib.common_config import (
|
||||
start_topology,
|
||||
write_test_header,
|
||||
write_test_footer,
|
||||
reset_config_on_routers,
|
||||
step,
|
||||
shutdown_bringup_interface,
|
||||
kill_router_daemons,
|
||||
start_router_daemons,
|
||||
create_static_routes,
|
||||
check_router_status,
|
||||
socat_send_igmp_join_traffic,
|
||||
topo_daemons
|
||||
)
|
||||
from lib.pim import (
|
||||
create_pim_config,
|
||||
verify_igmp_groups,
|
||||
verify_upstream_iif,
|
||||
verify_join_state_and_timer,
|
||||
verify_mroutes,
|
||||
verify_pim_neighbors,
|
||||
verify_pim_interface_traffic,
|
||||
verify_pim_rp_info,
|
||||
verify_pim_state,
|
||||
clear_pim_interface_traffic,
|
||||
clear_igmp_interfaces,
|
||||
clear_pim_interfaces,
|
||||
clear_mroute,
|
||||
clear_mroute_verify,
|
||||
)
|
||||
from lib.topolog import logger
|
||||
from lib.topojson import build_topo_from_json, build_config_from_json
|
||||
|
||||
# Global variables
|
||||
GROUP_RANGE_V6 = "ff08::/64"
|
||||
IGMP_JOIN_V6 = "ff08::1"
|
||||
STAR = "*"
|
||||
SOURCE = "Static"
|
||||
|
||||
pytestmark = [pytest.mark.pimd]
|
||||
|
||||
|
||||
def build_topo(tgen):
|
||||
"""Build function"""
|
||||
|
||||
# Building topology from json file
|
||||
build_topo_from_json(tgen, TOPO)
|
||||
|
||||
|
||||
def setup_module(mod):
|
||||
"""
|
||||
Sets up the pytest environment
|
||||
|
||||
* `mod`: module name
|
||||
"""
|
||||
|
||||
testsuite_run_time = time.asctime(time.localtime(time.time()))
|
||||
logger.info("Testsuite start time: %s", testsuite_run_time)
|
||||
logger.info("=" * 40)
|
||||
|
||||
topology = """
|
||||
|
||||
_______r2_____
|
||||
| |
|
||||
iperf | | iperf
|
||||
r0-----r1-------------r3-----r5
|
||||
| |
|
||||
|_____________|
|
||||
r4
|
||||
|
||||
"""
|
||||
logger.info("Master Topology: \n %s", topology)
|
||||
|
||||
logger.info("Running setup_module to create topology")
|
||||
|
||||
# This function initiates the topology build with Topogen...
|
||||
json_file = "{}/multicast_pimv6_static_rp.json".format(CWD)
|
||||
tgen = Topogen(json_file, mod.__name__)
|
||||
global TOPO
|
||||
TOPO = tgen.json_topo
|
||||
|
||||
# ... and here it calls Mininet initialization functions.
|
||||
|
||||
# get list of daemons needs to be started for this suite.
|
||||
daemons = topo_daemons(tgen, TOPO)
|
||||
|
||||
# Starting topology, create tmp files which are loaded to routers
|
||||
# to start daemons and then start routers
|
||||
start_topology(tgen, daemons)
|
||||
|
||||
# Don"t run this test if we have any failure.
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
# Creating configuration from JSON
|
||||
build_config_from_json(tgen, TOPO)
|
||||
|
||||
# Verify PIM neighbors
|
||||
result = verify_pim_neighbors(tgen, TOPO)
|
||||
assert result is True, "setup_module :Failed \n Error:" " {}".format(result)
|
||||
|
||||
logger.info("Running setup_module() done")
|
||||
|
||||
|
||||
def teardown_module():
|
||||
"""Teardown the pytest environment"""
|
||||
|
||||
logger.info("Running teardown_module to delete topology")
|
||||
tgen = get_topogen()
|
||||
|
||||
# Stop toplogy and Remove tmp files
|
||||
tgen.stop_topology()
|
||||
|
||||
logger.info("Testsuite end time: %s", time.asctime(time.localtime(time.time())))
|
||||
logger.info("=" * 40)
|
||||
|
||||
|
||||
#####################################################
|
||||
#
|
||||
# Testcases
|
||||
#
|
||||
#####################################################
|
||||
|
||||
|
||||
def verify_state_incremented(state_before, state_after):
|
||||
"""
|
||||
API to compare interface traffic state incrementing
|
||||
|
||||
Parameters
|
||||
----------
|
||||
* `state_before` : State dictionary for any particular instance
|
||||
* `state_after` : State dictionary for any particular instance
|
||||
"""
|
||||
|
||||
for router, state_data in state_before.items():
|
||||
for state, value in state_data.items():
|
||||
if state_before[router][state] >= state_after[router][state]:
|
||||
errormsg = (
|
||||
"[DUT: %s]: state %s value has not"
|
||||
" incremented, Initial value: %s, "
|
||||
"Current value: %s [FAILED!!]"
|
||||
% (
|
||||
router,
|
||||
state,
|
||||
state_before[router][state],
|
||||
state_after[router][state],
|
||||
)
|
||||
)
|
||||
return errormsg
|
||||
|
||||
logger.info(
|
||||
"[DUT: %s]: State %s value is "
|
||||
"incremented, Initial value: %s, Current value: %s"
|
||||
" [PASSED!!]",
|
||||
router,
|
||||
state,
|
||||
state_before[router][state],
|
||||
state_after[router][state],
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
#####################################################
|
||||
|
||||
def test_pimv6_add_delete_static_RP_p0(request):
|
||||
"""
|
||||
TC_1: Verify upstream interfaces(IIF) and join state are updated
|
||||
properly after adding and deleting the static RP
|
||||
TC_2: Verify IIF and OIL in "show ip pim state" updated properly
|
||||
after adding and deleting the static RP
|
||||
TC_3: (*, G) Mroute entry are cleared when static RP gets deleted
|
||||
TC_4: Verify (*,G) prune is send towards the RP after deleting the
|
||||
static RP
|
||||
|
||||
TOPOlogy used:
|
||||
r0------r1-----r2
|
||||
iperf DUT RP
|
||||
"""
|
||||
|
||||
tgen = get_topogen()
|
||||
tc_name = request.node.name
|
||||
write_test_header(tc_name)
|
||||
|
||||
# Don"t run this test if we have any failure.
|
||||
if tgen.routers_have_failure():
|
||||
check_router_status(tgen)
|
||||
|
||||
step("Shut link b/w R1 and R3")
|
||||
intf = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
|
||||
shutdown_bringup_interface(tgen, "r1", intf, ifaceaction=False)
|
||||
|
||||
step("Enable PIM between r1 and r2")
|
||||
step("Enable MLD on r1 interface and send IGMP " "join (FF08::1) to r1")
|
||||
step("Configure r2 loopback interface as RP")
|
||||
input_dict = {
|
||||
"r2": {
|
||||
"pim6": {
|
||||
"rp": [
|
||||
{
|
||||
"rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split(
|
||||
"/"
|
||||
)[0],
|
||||
"group_addr_range": GROUP_RANGE_V6,
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = create_pim_config(tgen, TOPO, input_dict)
|
||||
assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
|
||||
|
||||
step("Verify show ip pim interface traffic without any mld join")
|
||||
state_dict = {
|
||||
"r1": {TOPO["routers"]["r1"]["links"]["r2"]["interface"]: ["pruneTx"]}
|
||||
}
|
||||
|
||||
state_before = verify_pim_interface_traffic(tgen, state_dict, addr_type="ipv6")
|
||||
assert isinstance(
|
||||
state_before, dict
|
||||
), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
|
||||
tc_name, result
|
||||
)
|
||||
|
||||
step("send mld join (FF08::1) to R1")
|
||||
intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"]
|
||||
intf_ip = TOPO["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0]
|
||||
result = socat_send_igmp_join_traffic(
|
||||
tgen, "r0", "UDP6-RECV", IGMP_JOIN_V6, intf, intf_ip, join=True
|
||||
)
|
||||
assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
|
||||
|
||||
step("r1: Verify RP info")
|
||||
dut = "r1"
|
||||
oif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
|
||||
iif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
|
||||
rp_address = TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
|
||||
result = verify_pim_rp_info(
|
||||
tgen, TOPO, dut, GROUP_RANGE_V6, oif, rp_address, SOURCE
|
||||
)
|
||||
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
|
||||
|
||||
step("r1: Verify upstream IIF interface")
|
||||
result = verify_upstream_iif(tgen, dut, oif, STAR, IGMP_JOIN_V6)
|
||||
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
|
||||
|
||||
step("r1: Verify upstream join state and join timer")
|
||||
result = verify_join_state_and_timer(tgen, dut, oif, STAR, IGMP_JOIN_V6)
|
||||
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
|
||||
|
||||
step("r1: Verify PIM state")
|
||||
result = verify_pim_state(tgen, dut, oif, iif, IGMP_JOIN_V6)
|
||||
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
|
||||
|
||||
step("r1: Verify ip mroutes")
|
||||
result = verify_mroutes(tgen, dut, STAR, IGMP_JOIN_V6, oif, iif)
|
||||
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
|
||||
|
||||
step("r1: Delete RP configuration")
|
||||
input_dict = {
|
||||
"r2": {
|
||||
"pim6": {
|
||||
"rp": [
|
||||
{
|
||||
"rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split(
|
||||
"/"
|
||||
)[0],
|
||||
"group_addr_range": GROUP_RANGE_V6,
|
||||
"delete": True,
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = create_pim_config(tgen, TOPO, input_dict)
|
||||
assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
|
||||
|
||||
step("r1: Verify RP info")
|
||||
result = verify_pim_rp_info(
|
||||
tgen, TOPO, dut, GROUP_RANGE_V6, oif, rp_address, SOURCE, expected=False
|
||||
)
|
||||
assert (
|
||||
result is not True
|
||||
), "Testcase {} :Failed \n " "RP: {} info is still present \n Error: {}".format(
|
||||
tc_name, rp_address, result
|
||||
)
|
||||
|
||||
step("r1: Verify upstream IIF interface")
|
||||
result = verify_upstream_iif(tgen, dut, oif, STAR, IGMP_JOIN_V6, expected=False)
|
||||
assert result is not True, (
|
||||
"Testcase {} :Failed \n "
|
||||
"Upstream ({}, {}) is still in join state \n Error: {}".format(
|
||||
tc_name, STAR, IGMP_JOIN_V6, result
|
||||
)
|
||||
)
|
||||
|
||||
step("r1: Verify upstream join state and join timer")
|
||||
result = verify_join_state_and_timer(
|
||||
tgen, dut, oif, STAR, IGMP_JOIN_V6, expected=False
|
||||
)
|
||||
assert result is not True, (
|
||||
"Testcase {} :Failed \n "
|
||||
"Upstream ({}, {}) timer is still running \n Error: {}".format(
|
||||
tc_name, STAR, IGMP_JOIN_V6, result
|
||||
)
|
||||
)
|
||||
|
||||
step("r1: Verify PIM state")
|
||||
result = verify_pim_state(tgen, dut, oif, iif, IGMP_JOIN_V6, expected=False)
|
||||
assert result is not True, (
|
||||
"Testcase {} :Failed \n "
|
||||
"PIM state for group: {} is still Active \n Error: {}".format(
|
||||
tc_name, IGMP_JOIN_V6, result
|
||||
)
|
||||
)
|
||||
|
||||
step("r1: Verify ip mroutes")
|
||||
result = verify_mroutes(tgen, dut, STAR, IGMP_JOIN_V6, oif, iif, expected=False)
|
||||
assert result is not True, (
|
||||
"Testcase {} :Failed \n "
|
||||
"mroute ({}, {}) is still present \n Error: {}".format(
|
||||
tc_name, STAR, IGMP_JOIN_V6, result
|
||||
)
|
||||
)
|
||||
|
||||
step("r1: Verify show ip pim interface traffic without any IGMP join")
|
||||
state_after = verify_pim_interface_traffic(tgen, state_dict, addr_type="ipv6")
|
||||
assert isinstance(
|
||||
state_after, dict
|
||||
), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
|
||||
tc_name, result
|
||||
)
|
||||
|
||||
result = verify_state_incremented(state_before, state_after)
|
||||
assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
|
||||
|
||||
write_test_footer(tc_name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = ["-s"] + sys.argv[1:]
|
||||
sys.exit(pytest.main(args))
|
Loading…
Reference in New Issue
Block a user