mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-16 06:53:03 +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")
|
daemons.append("zebra")
|
||||||
if "pimd" in result:
|
if "pimd" in result:
|
||||||
daemons.append("pimd")
|
daemons.append("pimd")
|
||||||
|
if "pim6d" in result:
|
||||||
|
daemons.append("pim6d")
|
||||||
if "ospfd" in result:
|
if "ospfd" in result:
|
||||||
daemons.append("ospfd")
|
daemons.append("ospfd")
|
||||||
if "ospf6d" in result:
|
if "ospf6d" in result:
|
||||||
@ -1035,6 +1037,12 @@ def start_topology(tgen, daemon=None):
|
|||||||
TopoRouter.RD_PIM, "{}/{}/pimd.conf".format(tgen.logdir, rname)
|
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
|
# Starting routers
|
||||||
logger.info("Starting all routers once topology is created")
|
logger.info("Starting all routers once topology is created")
|
||||||
tgen.start_router()
|
tgen.start_router()
|
||||||
@ -1131,6 +1139,8 @@ def topo_daemons(tgen, topo=None):
|
|||||||
for val in topo["routers"][rtr]["links"].values():
|
for val in topo["routers"][rtr]["links"].values():
|
||||||
if "pim" in val and "pimd" not in daemon_list:
|
if "pim" in val and "pimd" not in daemon_list:
|
||||||
daemon_list.append("pimd")
|
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:
|
if "ospf" in val and "ospfd" not in daemon_list:
|
||||||
daemon_list.append("ospfd")
|
daemon_list.append("ospfd")
|
||||||
if "ospf6" in val and "ospf6d" not in daemon_list:
|
if "ospf6" in val and "ospf6d" not in daemon_list:
|
||||||
@ -3234,6 +3244,86 @@ def configure_interface_mac(tgen, input_dict):
|
|||||||
return True
|
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
|
# Verification APIs
|
||||||
#############################################
|
#############################################
|
||||||
|
@ -36,6 +36,7 @@ from lib.common_config import (
|
|||||||
InvalidCLIError,
|
InvalidCLIError,
|
||||||
retry,
|
retry,
|
||||||
run_frr_cmd,
|
run_frr_cmd,
|
||||||
|
validate_ip_address,
|
||||||
)
|
)
|
||||||
from lib.micronet import get_exec_path
|
from lib.micronet import get_exec_path
|
||||||
from lib.topolog import logger
|
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):
|
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
|
Parameters
|
||||||
----------
|
----------
|
||||||
@ -70,6 +71,16 @@ def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True
|
|||||||
"prefix-list": "pf_list_1"
|
"prefix-list": "pf_list_1"
|
||||||
"delete": True
|
"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
|
# Now add RP config to all routers
|
||||||
for router in input_dict.keys():
|
for router in input_dict.keys():
|
||||||
if "pim" not in input_dict[router]:
|
if "pim" in input_dict[router] or "pim6" in input_dict[router]:
|
||||||
continue
|
_add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict)
|
||||||
if "rp" not in input_dict[router]["pim"]:
|
|
||||||
continue
|
|
||||||
_add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = create_common_configurations(
|
result = create_common_configurations(
|
||||||
tgen, config_data_dict, "pim", build, load_config
|
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))
|
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
|
||||||
|
rp_data = []
|
||||||
|
|
||||||
pim_data = input_dict[router]["pim"]
|
# PIMv4
|
||||||
rp_data = pim_data["rp"]
|
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.
|
# Configure this RP on every router.
|
||||||
for dut in tgen.routers():
|
for dut in tgen.routers():
|
||||||
# At least one interface must be enabled for PIM on the router
|
# At least one interface must be enabled for PIM on the router
|
||||||
pim_if_enabled = False
|
pim_if_enabled = False
|
||||||
|
pim6_if_enabled = False
|
||||||
for destLink, data in topo[dut]["links"].items():
|
for destLink, data in topo[dut]["links"].items():
|
||||||
if "pim" in data:
|
if "pim" in data:
|
||||||
pim_if_enabled = True
|
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
|
continue
|
||||||
|
|
||||||
config_data = []
|
config_data = []
|
||||||
|
|
||||||
for rp_dict in deepcopy(rp_data):
|
if rp_data:
|
||||||
# ip address of RP
|
for rp_dict in deepcopy(rp_data):
|
||||||
if "rp_addr" not in rp_dict and build:
|
# ip address of RP
|
||||||
logger.error(
|
if "rp_addr" not in rp_dict and build:
|
||||||
"Router %s: 'ip address of RP' not " "present in input_dict/JSON",
|
logger.error(
|
||||||
router,
|
"Router %s: 'ip address of RP' not "
|
||||||
)
|
"present in input_dict/JSON",
|
||||||
|
router,
|
||||||
|
)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
rp_addr = rp_dict.setdefault("rp_addr", None)
|
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
|
# Group Address range to cover
|
||||||
keep_alive_timer = rp_dict.setdefault("keep_alive_timer", None)
|
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
|
return False
|
||||||
if "group_addr_range" not in rp_dict and build:
|
group_addr_range = rp_dict.setdefault("group_addr_range", None)
|
||||||
logger.error(
|
|
||||||
"Router %s:'Group Address range to cover'"
|
|
||||||
" not present in input_dict/JSON",
|
|
||||||
router,
|
|
||||||
)
|
|
||||||
|
|
||||||
return False
|
# Group prefix-list filter
|
||||||
group_addr_range = rp_dict.setdefault("group_addr_range", None)
|
prefix_list = rp_dict.setdefault("prefix_list", None)
|
||||||
|
|
||||||
# Group prefix-list filter
|
# Delete rp config
|
||||||
prefix_list = rp_dict.setdefault("prefix_list", None)
|
del_action = rp_dict.setdefault("delete", False)
|
||||||
|
|
||||||
# Delete rp config
|
if keep_alive_timer:
|
||||||
del_action = rp_dict.setdefault("delete", False)
|
if addr_type == "ipv4":
|
||||||
|
cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer)
|
||||||
if keep_alive_timer:
|
if del_action:
|
||||||
cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer)
|
cmd = "no {}".format(cmd)
|
||||||
if del_action:
|
config_data.append(cmd)
|
||||||
cmd = "no {}".format(cmd)
|
if addr_type == "ipv6":
|
||||||
config_data.append(cmd)
|
cmd = "ipv6 pim rp keep-alive-timer {}".format(keep_alive_timer)
|
||||||
|
|
||||||
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 del_action:
|
if del_action:
|
||||||
cmd = "no {}".format(cmd)
|
cmd = "no {}".format(cmd)
|
||||||
config_data.append(cmd)
|
config_data.append(cmd)
|
||||||
|
|
||||||
if prefix_list:
|
if rp_addr:
|
||||||
cmd = "ip pim rp {} prefix-list {}".format(rp_addr, prefix_list)
|
if group_addr_range:
|
||||||
if del_action:
|
if type(group_addr_range) is not list:
|
||||||
cmd = "no {}".format(cmd)
|
group_addr_range = [group_addr_range]
|
||||||
config_data.append(cmd)
|
|
||||||
|
|
||||||
if config_data:
|
for grp_addr in group_addr_range:
|
||||||
if dut not in config_data_dict:
|
if addr_type == "ipv4":
|
||||||
config_data_dict[dut] = config_data
|
cmd = "ip pim rp {} {}".format(rp_addr, grp_addr)
|
||||||
else:
|
if del_action:
|
||||||
config_data_dict[dut].extend(config_data)
|
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):
|
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
|
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):
|
def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False):
|
||||||
"""
|
"""
|
||||||
Helper API to enable or disable pim on interfaces
|
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 = []
|
config_data = []
|
||||||
|
|
||||||
# Enable pim on interfaces
|
# Enable pim/pim6 on interfaces
|
||||||
for destRouterLink, data in sorted(topo[router]["links"].items()):
|
for destRouterLink, data in sorted(topo[router]["links"].items()):
|
||||||
if "pim" in data and data["pim"] == "enable":
|
if "pim" in data and data["pim"] == "enable":
|
||||||
# Loopback interfaces
|
# 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(cmd)
|
||||||
config_data.append("ip pim")
|
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
|
# pim global config
|
||||||
if "pim" in input_dict[router]:
|
if "pim" in input_dict[router]:
|
||||||
pim_data = input_dict[router]["pim"]
|
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)
|
cmd = "no {}".format(cmd)
|
||||||
config_data.append(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
|
return config_data
|
||||||
|
|
||||||
|
|
||||||
@ -732,9 +922,6 @@ def verify_upstream_iif(
|
|||||||
"[DUT: %s]: Verifying upstream Inbound Interface" " for IGMP groups received:",
|
"[DUT: %s]: Verifying upstream Inbound Interface" " for IGMP groups received:",
|
||||||
dut,
|
dut,
|
||||||
)
|
)
|
||||||
show_ip_pim_upstream_json = run_frr_cmd(
|
|
||||||
rnode, "show ip pim upstream json", isjson=True
|
|
||||||
)
|
|
||||||
|
|
||||||
if type(group_addresses) is not list:
|
if type(group_addresses) is not list:
|
||||||
group_addresses = [group_addresses]
|
group_addresses = [group_addresses]
|
||||||
@ -742,6 +929,17 @@ def verify_upstream_iif(
|
|||||||
if type(iif) is not list:
|
if type(iif) is not list:
|
||||||
iif = [iif]
|
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:
|
for grp_addr in group_addresses:
|
||||||
# Verify group address
|
# Verify group address
|
||||||
if grp_addr not in show_ip_pim_upstream_json:
|
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: %s]: Verifying Join state and Join Timer" " for IGMP groups received:",
|
||||||
dut,
|
dut,
|
||||||
)
|
)
|
||||||
show_ip_pim_upstream_json = run_frr_cmd(
|
|
||||||
rnode, "show ip pim upstream json", isjson=True
|
|
||||||
)
|
|
||||||
|
|
||||||
if type(group_addresses) is not list:
|
if type(group_addresses) is not list:
|
||||||
group_addresses = [group_addresses]
|
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:
|
for grp_addr in group_addresses:
|
||||||
# Verify group address
|
# Verify group address
|
||||||
if grp_addr not in show_ip_pim_upstream_json:
|
if grp_addr not in show_ip_pim_upstream_json:
|
||||||
@ -1010,20 +1214,6 @@ def verify_mroutes(
|
|||||||
|
|
||||||
rnode = tgen.routers()[dut]
|
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):
|
if not isinstance(group_addresses, list):
|
||||||
group_addresses = [group_addresses]
|
group_addresses = [group_addresses]
|
||||||
|
|
||||||
@ -1033,6 +1223,30 @@ def verify_mroutes(
|
|||||||
if not isinstance(oil, list) and oil != "none":
|
if not isinstance(oil, list) and oil != "none":
|
||||||
oil = [oil]
|
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:
|
for grp_addr in group_addresses:
|
||||||
if grp_addr not in show_ip_mroute_json:
|
if grp_addr not in show_ip_mroute_json:
|
||||||
errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
|
errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
|
||||||
@ -1214,15 +1428,20 @@ def verify_pim_rp_info(
|
|||||||
|
|
||||||
rnode = tgen.routers()[dut]
|
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:
|
if type(group_addresses) is not list:
|
||||||
group_addresses = [group_addresses]
|
group_addresses = [group_addresses]
|
||||||
|
|
||||||
if type(oif) is not list:
|
if type(oif) is not list:
|
||||||
oif = [oif]
|
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:
|
for grp_addr in group_addresses:
|
||||||
if rp is None:
|
if rp is None:
|
||||||
rp_details = find_rp_details(tgen, topo)
|
rp_details = find_rp_details(tgen, topo)
|
||||||
@ -1232,9 +1451,14 @@ def verify_pim_rp_info(
|
|||||||
else:
|
else:
|
||||||
iamRP = False
|
iamRP = False
|
||||||
else:
|
else:
|
||||||
show_ip_route_json = run_frr_cmd(
|
if addr_type == "ipv4":
|
||||||
rnode, "show ip route connected json", isjson=True
|
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():
|
for _rp in show_ip_route_json.keys():
|
||||||
if rp == _rp.split("/")[0]:
|
if rp == _rp.split("/")[0]:
|
||||||
iamRP = True
|
iamRP = True
|
||||||
@ -1242,16 +1466,27 @@ def verify_pim_rp_info(
|
|||||||
else:
|
else:
|
||||||
iamRP = False
|
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:
|
if rp not in show_ip_rp_info_json:
|
||||||
errormsg = "[DUT %s]: Verifying rp-info" "for rp_address %s [FAILED]!! " % (
|
errormsg = (
|
||||||
dut,
|
"[DUT %s]: Verifying rp-info "
|
||||||
rp,
|
"for rp_address %s [FAILED]!! " % (dut, rp)
|
||||||
)
|
)
|
||||||
return errormsg
|
return errormsg
|
||||||
else:
|
else:
|
||||||
group_addr_json = show_ip_rp_info_json[rp]
|
group_addr_json = show_ip_rp_info_json[rp]
|
||||||
|
|
||||||
for rp_json in group_addr_json:
|
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:
|
if oif is not None:
|
||||||
found = False
|
found = False
|
||||||
if rp_json["outboundInterface"] not in oif:
|
if rp_json["outboundInterface"] not in oif:
|
||||||
@ -1380,14 +1615,26 @@ def verify_pim_state(
|
|||||||
rnode = tgen.routers()[dut]
|
rnode = tgen.routers()[dut]
|
||||||
|
|
||||||
logger.info("[DUT: %s]: Verifying pim state", 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:
|
if type(group_addresses) is not list:
|
||||||
group_addresses = [group_addresses]
|
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:
|
for grp_addr in group_addresses:
|
||||||
if src_address is None:
|
if src_address is None:
|
||||||
src_address = "*"
|
src_address = "*"
|
||||||
@ -3635,7 +3882,7 @@ def verify_local_igmp_groups(tgen, dut, interface, group_addresses):
|
|||||||
return True
|
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
|
Verify ip pim interface traffice by running
|
||||||
"show ip pim interface traffic" cli
|
"show ip pim interface traffic" cli
|
||||||
@ -3645,6 +3892,8 @@ def verify_pim_interface_traffic(tgen, input_dict, return_stats=True):
|
|||||||
* `tgen`: topogen object
|
* `tgen`: topogen object
|
||||||
* `input_dict(dict)`: defines DUT, what and from which interfaces
|
* `input_dict(dict)`: defines DUT, what and from which interfaces
|
||||||
traffic needs to be verified
|
traffic needs to be verified
|
||||||
|
* [optional]`addr_type`: specify address-family, default is ipv4
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
input_dict = {
|
input_dict = {
|
||||||
@ -3675,9 +3924,13 @@ def verify_pim_interface_traffic(tgen, input_dict, return_stats=True):
|
|||||||
rnode = tgen.routers()[dut]
|
rnode = tgen.routers()[dut]
|
||||||
|
|
||||||
logger.info("[DUT: %s]: Verifying pim interface traffic", 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] = {}
|
output_dict[dut] = {}
|
||||||
for intf, data in input_dict[dut].items():
|
for intf, data in input_dict[dut].items():
|
||||||
|
@ -725,6 +725,7 @@ class TopoRouter(TopoGear):
|
|||||||
RD_PBRD = 16
|
RD_PBRD = 16
|
||||||
RD_PATH = 17
|
RD_PATH = 17
|
||||||
RD_SNMP = 18
|
RD_SNMP = 18
|
||||||
|
RD_PIM6 = 19
|
||||||
RD = {
|
RD = {
|
||||||
RD_FRR: "frr",
|
RD_FRR: "frr",
|
||||||
RD_ZEBRA: "zebra",
|
RD_ZEBRA: "zebra",
|
||||||
@ -735,6 +736,7 @@ class TopoRouter(TopoGear):
|
|||||||
RD_ISIS: "isisd",
|
RD_ISIS: "isisd",
|
||||||
RD_BGP: "bgpd",
|
RD_BGP: "bgpd",
|
||||||
RD_PIM: "pimd",
|
RD_PIM: "pimd",
|
||||||
|
RD_PIM6: "pim6d",
|
||||||
RD_LDP: "ldpd",
|
RD_LDP: "ldpd",
|
||||||
RD_EIGRP: "eigrpd",
|
RD_EIGRP: "eigrpd",
|
||||||
RD_NHRP: "nhrpd",
|
RD_NHRP: "nhrpd",
|
||||||
@ -820,7 +822,8 @@ class TopoRouter(TopoGear):
|
|||||||
Possible daemon values are: TopoRouter.RD_ZEBRA, TopoRouter.RD_RIP,
|
Possible daemon values are: TopoRouter.RD_ZEBRA, TopoRouter.RD_RIP,
|
||||||
TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6,
|
TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6,
|
||||||
TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP,
|
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
|
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
|
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",
|
"ripngd",
|
||||||
"isisd",
|
"isisd",
|
||||||
"pimd",
|
"pimd",
|
||||||
|
"pim6d",
|
||||||
"ldpd",
|
"ldpd",
|
||||||
"pbrd",
|
"pbrd",
|
||||||
]:
|
]:
|
||||||
|
@ -41,7 +41,11 @@ from lib.common_config import (
|
|||||||
number_to_column,
|
number_to_column,
|
||||||
)
|
)
|
||||||
from lib.ospf import create_router_ospf
|
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
|
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),
|
("route_maps", create_route_maps),
|
||||||
("pim", create_pim_config),
|
("pim", create_pim_config),
|
||||||
("igmp", create_igmp_config),
|
("igmp", create_igmp_config),
|
||||||
|
("mld", create_mld_config),
|
||||||
("bgp", create_router_bgp),
|
("bgp", create_router_bgp),
|
||||||
("ospf", create_router_ospf),
|
("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")
|
logger.info("build_config_from_json: failed to configure topology")
|
||||||
pytest.exit(1)
|
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 ospf in ["ospf", "ospf6"]:
|
||||||
for router in data:
|
for router in data:
|
||||||
if ospf not in data[router]:
|
if ospf not in data[router]:
|
||||||
|
@ -1330,6 +1330,7 @@ class Router(Node):
|
|||||||
"isisd": 0,
|
"isisd": 0,
|
||||||
"bgpd": 0,
|
"bgpd": 0,
|
||||||
"pimd": 0,
|
"pimd": 0,
|
||||||
|
"pim6d": 0,
|
||||||
"ldpd": 0,
|
"ldpd": 0,
|
||||||
"eigrpd": 0,
|
"eigrpd": 0,
|
||||||
"nhrpd": 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