summaryrefslogtreecommitdiff
path: root/tests/topotests/lib/common_config.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/topotests/lib/common_config.py')
-rw-r--r--tests/topotests/lib/common_config.py750
1 files changed, 641 insertions, 109 deletions
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index 6c24b6ddbb..3f360ef40a 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -31,7 +31,6 @@ from re import search as re_search
from tempfile import mkdtemp
import os
-import io
import sys
import traceback
import socket
@@ -39,6 +38,7 @@ import ipaddress
import platform
if sys.version_info[0] > 2:
+ import io
import configparser
else:
import StringIO
@@ -151,8 +151,8 @@ class InvalidCLIError(Exception):
def run_frr_cmd(rnode, cmd, isjson=False):
"""
- Execute frr show commands in priviledged mode
- * `rnode`: router node on which commands needs to executed
+ Execute frr show commands in privileged mode
+ * `rnode`: router node on which command needs to be executed
* `cmd`: Command to be executed on frr
* `isjson`: If command is to get json data or not
:return str:
@@ -184,11 +184,11 @@ def apply_raw_config(tgen, input_dict):
"""
API to configure raw configuration on device. This can be used for any cli
- which does has not been implemented in JSON.
+ which has not been implemented in JSON.
Parameters
----------
- * `tgen`: tgen onject
+ * `tgen`: tgen object
* `input_dict`: configuration that needs to be applied
Usage
@@ -232,8 +232,8 @@ def create_common_configuration(
frr_json.conf and load to router
Parameters
----------
- * `tgen`: tgen onject
- * `data`: Congiguration data saved in a list.
+ * `tgen`: tgen object
+ * `data`: Configuration data saved in a list.
* `router` : router id to be configured.
* `config_type` : Syntactic information while writing configuration. Should
be one of the value as mentioned in the config_map below.
@@ -257,6 +257,8 @@ def create_common_configuration(
"bgp": "! BGP Config\n",
"vrf": "! VRF Config\n",
"ospf": "! OSPF Config\n",
+ "ospf6": "! OSPF Config\n",
+ "pim": "! PIM Config\n",
}
)
@@ -292,8 +294,8 @@ def create_common_configuration(
def kill_router_daemons(tgen, router, daemons):
"""
- Router's current config would be saved to /etc/frr/ for each deamon
- and deamon would be killed forcefully using SIGKILL.
+ Router's current config would be saved to /etc/frr/ for each daemon
+ and daemon would be killed forcefully using SIGKILL.
* `tgen` : topogen object
* `router`: Device under test
* `daemons`: list of daemons to be killed
@@ -389,6 +391,8 @@ def check_router_status(tgen):
daemons.append("bgpd")
if "zebra" in result:
daemons.append("zebra")
+ if "pimd" in result:
+ daemons.append("pimd")
rnode.startDaemons(daemons)
@@ -593,13 +597,13 @@ def load_config_to_router(tgen, routerName, save_bkup=False):
def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None):
"""
- API to get the link local ipv6 address of a perticular interface using
+ API to get the link local ipv6 address of a particular interface using
FRR command 'show interface'
- * `tgen`: tgen onject
- * `router` : router for which hightest interface should be
+ * `tgen`: tgen object
+ * `router` : router for which highest interface should be
calculated
- * `intf` : interface for which linklocal address needs to be taken
+ * `intf` : interface for which link-local address needs to be taken
* `vrf` : VRF name
Usage
@@ -688,7 +692,7 @@ def generate_support_bundle():
def start_topology(tgen, daemon=None):
"""
Starting topology, create tmp files which are loaded to routers
- to start deamons and then start routers
+ to start daemons and then start routers
* `tgen` : topogen object
"""
@@ -696,7 +700,7 @@ def start_topology(tgen, daemon=None):
# Starting topology
tgen.start_topology()
- # Starting deamons
+ # Starting daemons
router_list = tgen.routers()
ROUTER_LIST = sorted(
@@ -735,28 +739,41 @@ def start_topology(tgen, daemon=None):
except IOError as err:
logger.error("I/O error({0}): {1}".format(err.errno, err.strerror))
- # Loading empty zebra.conf file to router, to start the zebra deamon
+ # Loading empty zebra.conf file to router, to start the zebra daemon
router.load_config(
TopoRouter.RD_ZEBRA, "{}/{}/zebra.conf".format(TMPDIR, rname)
)
- # Loading empty bgpd.conf file to router, to start the bgp deamon
+ # Loading empty bgpd.conf file to router, to start the bgp daemon
router.load_config(TopoRouter.RD_BGP, "{}/{}/bgpd.conf".format(TMPDIR, rname))
if daemon and "ospfd" in daemon:
- # Loading empty ospf.conf file to router, to start the bgp deamon
+ # Loading empty ospf.conf file to router, to start the bgp daemon
router.load_config(
TopoRouter.RD_OSPF, "{}/{}/ospfd.conf".format(TMPDIR, rname)
)
- # Starting routers
+
+ if daemon and "ospf6d" in daemon:
+ # Loading empty ospf.conf file to router, to start the bgp daemon
+ router.load_config(
+ TopoRouter.RD_OSPF6, "{}/{}/ospf6d.conf".format(TMPDIR, rname)
+ )
+
+ if daemon and "pimd" in daemon:
+ # Loading empty pimd.conf file to router, to start the pim deamon
+ router.load_config(
+ TopoRouter.RD_PIM, "{}/{}/pimd.conf".format(TMPDIR, rname)
+ )
+
+ # Starting routers
logger.info("Starting all routers once topology is created")
tgen.start_router()
def stop_router(tgen, router):
"""
- Router"s current config would be saved to /etc/frr/ for each deamon
- and router and its deamons would be stopped.
+ Router"s current config would be saved to /tmp/topotest/<suite>/<router> for each daemon
+ and router and its daemons would be stopped.
* `tgen` : topogen object
* `router`: Device under test
@@ -774,8 +791,8 @@ def stop_router(tgen, router):
def start_router(tgen, router):
"""
- Router will started and config would be loaded from /etc/frr/ for each
- deamon
+ Router will be started and config would be loaded from /tmp/topotest/<suite>/<router> for each
+ daemon
* `tgen` : topogen object
* `router`: Device under test
@@ -786,8 +803,8 @@ def start_router(tgen, router):
try:
router_list = tgen.routers()
- # Router and its deamons would be started and config would
- # be loaded to router for each deamon from /etc/frr
+ # Router and its daemons would be started and config would
+ # be loaded to router for each daemon from /etc/frr
router_list[router].start()
# Waiting for router to come up
@@ -835,9 +852,204 @@ def topo_daemons(tgen, topo):
if "ospf" in topo["routers"][rtr] and "ospfd" not in daemon_list:
daemon_list.append("ospfd")
+ if "ospf6" in topo["routers"][rtr] and "ospf6d" not in daemon_list:
+ daemon_list.append("ospf6d")
+
+ for val in topo["routers"][rtr]["links"].values():
+ if "pim" in val and "pimd" not in daemon_list:
+ daemon_list.append("pimd")
+ break
+
return daemon_list
+def add_interfaces_to_vlan(tgen, input_dict):
+ """
+ Add interfaces to VLAN, we need vlan pakcage to be installed on machine
+
+ * `tgen`: tgen onject
+ * `input_dict` : interfaces to be added to vlans
+
+ input_dict= {
+ "r1":{
+ "vlan":{
+ VLAN_1: [{
+ intf_r1_s1: {
+ "ip": "10.1.1.1",
+ "subnet": "255.255.255.0
+ }
+ }]
+ }
+ }
+ }
+
+ add_interfaces_to_vlan(tgen, input_dict)
+
+ """
+
+ router_list = tgen.routers()
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ if "vlan" in input_dict[dut]:
+ for vlan, interfaces in input_dict[dut]["vlan"].items():
+ for intf_dict in interfaces:
+ for interface, data in intf_dict.items():
+ # Adding interface to VLAN
+ cmd = "vconfig add {} {}".format(interface, vlan)
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+ vlan_intf = "{}.{}".format(interface, vlan)
+
+ ip = data["ip"]
+ subnet = data["subnet"]
+
+ # Bringing interface up
+ cmd = "ip link set up {}".format(vlan_intf)
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+ # Assigning IP address
+ cmd = "ifconfig {} {} netmask {}".format(vlan_intf, ip, subnet)
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+
+def tcpdump_capture_start(
+ tgen,
+ router,
+ intf,
+ protocol=None,
+ grepstr=None,
+ timeout=0,
+ options=None,
+ cap_file=None,
+ background=True,
+):
+ """
+ API to capture network packets using tcp dump.
+
+ Packages used :
+
+ Parameters
+ ----------
+ * `tgen`: topogen object.
+ * `router`: router on which ping has to be performed.
+ * `intf` : interface for capture.
+ * `protocol` : protocol for which packet needs to be captured.
+ * `grepstr` : string to filter out tcp dump output.
+ * `timeout` : Time for which packet needs to be captured.
+ * `options` : options for TCP dump, all tcpdump options can be used.
+ * `cap_file` : filename to store capture dump.
+ * `background` : Make tcp dump run in back ground.
+
+ Usage
+ -----
+ tcpdump_result = tcpdump_dut(tgen, 'r2', intf, protocol='tcp', timeout=20,
+ options='-A -vv -x > r2bgp.txt ')
+ Returns
+ -------
+ 1) True for successful capture
+ 2) errormsg - when tcp dump fails
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.routers()[router]
+
+ if timeout > 0:
+ cmd = "timeout {}".format(timeout)
+ else:
+ cmd = ""
+
+ cmdargs = "{} tcpdump".format(cmd)
+
+ if intf:
+ cmdargs += " -i {}".format(str(intf))
+ if protocol:
+ cmdargs += " {}".format(str(protocol))
+ if options:
+ cmdargs += " -s 0 {}".format(str(options))
+
+ if cap_file:
+ file_name = os.path.join(LOGDIR, tgen.modname, router, cap_file)
+ cmdargs += " -w {}".format(str(file_name))
+ # Remove existing capture file
+ rnode.run("rm -rf {}".format(file_name))
+
+ if grepstr:
+ cmdargs += ' | grep "{}"'.format(str(grepstr))
+
+ logger.info("Running tcpdump command: [%s]", cmdargs)
+ if not background:
+ rnode.run(cmdargs)
+ else:
+ rnode.run("nohup {} & /dev/null 2>&1".format(cmdargs))
+
+ # Check if tcpdump process is running
+ if background:
+ result = rnode.run("pgrep tcpdump")
+ logger.debug("ps -ef | grep tcpdump \n {}".format(result))
+
+ if not result:
+ errormsg = "tcpdump is not running {}".format("tcpdump")
+ return errormsg
+ else:
+ logger.info("Packet capture started on %s: interface %s", router, intf)
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def tcpdump_capture_stop(tgen, router):
+ """
+ API to capture network packets using tcp dump.
+
+ Packages used :
+
+ Parameters
+ ----------
+ * `tgen`: topogen object.
+ * `router`: router on which ping has to be performed.
+ * `intf` : interface for capture.
+ * `protocol` : protocol for which packet needs to be captured.
+ * `grepstr` : string to filter out tcp dump output.
+ * `timeout` : Time for which packet needs to be captured.
+ * `options` : options for TCP dump, all tcpdump options can be used.
+ * `cap2file` : filename to store capture dump.
+ * `bakgrnd` : Make tcp dump run in back ground.
+
+ Usage
+ -----
+ tcpdump_result = tcpdump_dut(tgen, 'r2', intf, protocol='tcp', timeout=20,
+ options='-A -vv -x > r2bgp.txt ')
+ Returns
+ -------
+ 1) True for successful capture
+ 2) errormsg - when tcp dump fails
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.routers()[router]
+
+ # Check if tcpdump process is running
+ result = rnode.run("ps -ef | grep tcpdump")
+ logger.debug("ps -ef | grep tcpdump \n {}".format(result))
+
+ if not re_search(r"{}".format("tcpdump"), result):
+ errormsg = "tcpdump is not running {}".format("tcpdump")
+ return errormsg
+ else:
+ ppid = tgen.net.nameToNode[rnode.name].pid
+ rnode.run("set +m; pkill -P %s tcpdump &> /dev/null" % ppid)
+ logger.info("Stopped tcpdump capture")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
#############################################
# Common APIs, will be used by all protocols
#############################################
@@ -1137,11 +1349,11 @@ def generate_ips(network, no_of_ips):
"""
Returns list of IPs.
based on start_ip and no_of_ips
+
* `network` : from here the ip will start generating,
start_ip will be
* `no_of_ips` : these many IPs will be generated
"""
-
ipaddress_list = []
if type(network) is not list:
network = [network]
@@ -1152,14 +1364,20 @@ def generate_ips(network, no_of_ips):
mask = int(start_ipaddr.split("/")[1])
else:
logger.debug("start_ipaddr {} must have a / in it".format(start_ipaddr))
- assert(0)
+ assert 0
addr_type = validate_ip_address(start_ip)
if addr_type == "ipv4":
- start_ip = ipaddress.IPv4Address(frr_unicode(start_ip))
+ if start_ip == "0.0.0.0" and mask == 0 and no_of_ips == 1:
+ ipaddress_list.append("{}/{}".format(start_ip, mask))
+ return ipaddress_list
+ start_ip = ipaddress.IPv4Address(unicode(start_ip))
step = 2 ** (32 - mask)
if addr_type == "ipv6":
- start_ip = ipaddress.IPv6Address(frr_unicode(start_ip))
+ if start_ip == "0::0" and mask == 0 and no_of_ips == 1:
+ ipaddress_list.append("{}/{}".format(start_ip, mask))
+ return ipaddress_list
+ start_ip = ipaddress.IPv6Address(unicode(start_ip))
step = 2 ** (128 - mask)
next_ip = start_ip
@@ -1181,7 +1399,7 @@ def find_interface_with_greater_ip(topo, router, loopback=True, interface=True):
it will return highest IP from loopback IPs otherwise from physical
interface IPs.
* `topo` : json file data
- * `router` : router for which hightest interface should be calculated
+ * `router` : router for which highest interface should be calculated
"""
link_data = topo["routers"][router]["links"]
@@ -1287,7 +1505,6 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict
_wait = kwargs.pop("wait", wait)
_attempts = kwargs.pop("attempts", attempts)
_attempts = int(_attempts)
- expected = True
if _attempts < 0:
raise ValueError("attempts must be 0 or greater")
@@ -1297,12 +1514,10 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict
_return_is_str = kwargs.pop("return_is_str", return_is_str)
_return_is_dict = kwargs.pop("return_is_str", return_is_dict)
+ _expected = kwargs.setdefault("expected", True)
+ kwargs.pop("expected")
for i in range(1, _attempts + 1):
try:
- _expected = kwargs.setdefault("expected", True)
- if _expected is False:
- expected = _expected
- kwargs.pop("expected")
ret = func(*args, **kwargs)
logger.debug("Function returned %s", ret)
if _return_is_str and isinstance(ret, bool) and _expected:
@@ -1314,11 +1529,11 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict
if _return_is_dict and isinstance(ret, dict):
return ret
- if _attempts == i and expected:
+ if _attempts == i:
generate_support_bundle()
return ret
except Exception as err:
- if _attempts == i and expected:
+ if _attempts == i:
generate_support_bundle()
logger.info("Max number of attempts (%r) reached", _attempts)
raise
@@ -1360,6 +1575,17 @@ def step(msg, reset=False):
_step(msg, reset)
+def do_countdown(secs):
+ """
+ Countdown timer display
+ """
+ for i in range(secs, 0, -1):
+ sys.stdout.write("{} ".format(str(i)))
+ sys.stdout.flush()
+ sleep(1)
+ return
+
+
#############################################
# These APIs, will used by testcase
#############################################
@@ -1378,6 +1604,25 @@ def create_interfaces_cfg(tgen, topo, build=False):
-------
True or False
"""
+
+ def _create_interfaces_ospf_cfg(ospf, c_data, data, ospf_keywords):
+ interface_data = []
+ ip_ospf = "ipv6 ospf6" if ospf == "ospf6" else "ip ospf"
+ for keyword in ospf_keywords:
+ if keyword in data[ospf]:
+ intf_ospf_value = c_data["links"][destRouterLink][ospf][keyword]
+ if "delete" in data and data["delete"]:
+ interface_data.append(
+ "no {} {}".format(ip_ospf, keyword.replace("_", "-"))
+ )
+ else:
+ interface_data.append(
+ "{} {} {}".format(
+ ip_ospf, keyword.replace("_", "-"), intf_ospf_value
+ )
+ )
+ return interface_data
+
result = False
topo = deepcopy(topo)
@@ -1424,66 +1669,26 @@ def create_interfaces_cfg(tgen, topo, build=False):
else:
interface_data.append("ipv6 address {}\n".format(intf_addr))
+ ospf_keywords = [
+ "hello_interval",
+ "dead_interval",
+ "network",
+ "priority",
+ "cost",
+ ]
if "ospf" in data:
- ospf_data = data["ospf"]
- if "area" in ospf_data:
- intf_ospf_area = c_data["links"][destRouterLink]["ospf"]["area"]
- if "delete" in data and data["delete"]:
- interface_data.append("no ip ospf area")
- else:
- interface_data.append(
- "ip ospf area {}".format(intf_ospf_area)
- )
-
- if "hello_interval" in ospf_data:
- intf_ospf_hello = c_data["links"][destRouterLink]["ospf"][
- "hello_interval"
- ]
- if "delete" in data and data["delete"]:
- interface_data.append("no ip ospf " " hello-interval")
- else:
- interface_data.append(
- "ip ospf " " hello-interval {}".format(intf_ospf_hello)
- )
-
- if "dead_interval" in ospf_data:
- intf_ospf_dead = c_data["links"][destRouterLink]["ospf"][
- "dead_interval"
- ]
- if "delete" in data and data["delete"]:
- interface_data.append("no ip ospf" " dead-interval")
- else:
- interface_data.append(
- "ip ospf " " dead-interval {}".format(intf_ospf_dead)
- )
-
- if "network" in ospf_data:
- intf_ospf_nw = c_data["links"][destRouterLink]["ospf"][
- "network"
- ]
- if "delete" in data and data["delete"]:
- interface_data.append(
- "no ip ospf" " network {}".format(intf_ospf_nw)
- )
- else:
- interface_data.append(
- "ip ospf" " network {}".format(intf_ospf_nw)
- )
-
- if "priority" in ospf_data:
- intf_ospf_nw = c_data["links"][destRouterLink]["ospf"][
- "priority"
- ]
+ interface_data += _create_interfaces_ospf_cfg(
+ "ospf", c_data, data, ospf_keywords + ["area"]
+ )
+ if "ospf6" in data:
+ interface_data += _create_interfaces_ospf_cfg(
+ "ospf6", c_data, data, ospf_keywords
+ )
- if "delete" in data and data["delete"]:
- interface_data.append("no ip ospf" " priority")
- else:
- interface_data.append(
- "ip ospf" " priority {}".format(intf_ospf_nw)
- )
result = create_common_configuration(
tgen, c_router, interface_data, "interface_config", build=build
)
+
except InvalidCLIError:
# Traceback
errormsg = traceback.format_exc()
@@ -2222,9 +2427,9 @@ def shutdown_bringup_interface(tgen, dut, intf_name, ifaceaction=False):
-----
dut = "r3"
intf = "r3-r1-eth0"
- # Shut down ineterface
+ # Shut down interface
shutdown_bringup_interface(tgen, dut, intf, False)
- # Bring up ineterface
+ # Bring up interface
shutdown_bringup_interface(tgen, dut, intf, True)
Returns
-------
@@ -2233,13 +2438,58 @@ def shutdown_bringup_interface(tgen, dut, intf_name, ifaceaction=False):
router_list = tgen.routers()
if ifaceaction:
- logger.info("Bringing up interface : {}".format(intf_name))
+ logger.info("Bringing up interface {} : {}".format(dut, intf_name))
else:
- logger.info("Shutting down interface : {}".format(intf_name))
+ logger.info("Shutting down interface {} : {}".format(dut, intf_name))
interface_set_status(router_list[dut], intf_name, ifaceaction)
+def stop_router(tgen, router):
+ """
+ Router's current config would be saved to /tmp/topotest/<suite>/<router>
+ for each daemon and router and its daemons would be stopped.
+
+ * `tgen` : topogen object
+ * `router`: Device under test
+ """
+
+ router_list = tgen.routers()
+
+ # Saving router config to /etc/frr, which will be loaded to router
+ # when it starts
+ router_list[router].vtysh_cmd("write memory")
+
+ # Stop router
+ router_list[router].stop()
+
+
+def start_router(tgen, router):
+ """
+ Router will be started and config would be loaded from
+ /tmp/topotest/<suite>/<router> for each daemon
+
+ * `tgen` : topogen object
+ * `router`: Device under test
+ """
+
+ logger.debug("Entering lib API: start_router")
+
+ try:
+ router_list = tgen.routers()
+
+ # Router and its daemons would be started and config would
+ # be loaded to router for each daemon from /etc/frr
+ router_list[router].start()
+
+ except Exception as e:
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: start_router()")
+
+
def addKernelRoute(
tgen, router, intf, group_addr_range, next_hop=None, src=None, del_action=None
):
@@ -2250,7 +2500,7 @@ def addKernelRoute(
-----------
* `tgen` : Topogen object
* `router`: router for which kernal routes needs to be added
- * `intf`: interface name, for which kernal routes needs to be added
+ * `intf`: interface name, for which kernel routes needs to be added
* `bindToAddress`: bind to <host>, an interface or multicast
address
@@ -2313,7 +2563,7 @@ def configure_vxlan(tgen, input_dict):
"""
Add and configure vxlan
- * `tgen`: tgen onject
+ * `tgen`: tgen object
* `input_dict` : data for vxlan config
Usage:
@@ -2414,7 +2664,7 @@ def configure_brctl(tgen, topo, input_dict):
"""
Add and configure brctl
- * `tgen`: tgen onject
+ * `tgen`: tgen object
* `input_dict` : data for brctl config
Usage:
@@ -2508,7 +2758,7 @@ def configure_interface_mac(tgen, input_dict):
"""
Add and configure brctl
- * `tgen`: tgen onject
+ * `tgen`: tgen object
* `input_dict` : data for mac config
input_mac= {
@@ -2562,7 +2812,7 @@ def verify_rib(
tag=None,
metric=None,
fib=None,
- count_only=False
+ count_only=False,
):
"""
Data will be read from input_dict or input JSON file, API will generate
@@ -2754,8 +3004,10 @@ def verify_rib(
"Nexthops are missing for "
"route {} in RIB of router {}: "
"expected {}, found {}\n".format(
- st_rt, dut, len(next_hop),
- len(found_hops)
+ st_rt,
+ dut,
+ len(next_hop),
+ len(found_hops),
)
)
return errormsg
@@ -2802,7 +3054,11 @@ def verify_rib(
errormsg = (
"[DUT: {}]: tag value {}"
" is not matched for"
- " route {} in RIB \n".format(dut, _tag, st_rt,)
+ " route {} in RIB \n".format(
+ dut,
+ _tag,
+ st_rt,
+ )
)
return errormsg
@@ -2819,7 +3075,11 @@ def verify_rib(
errormsg = (
"[DUT: {}]: metric value "
"{} is not matched for "
- "route {} in RIB \n".format(dut, metric, st_rt,)
+ "route {} in RIB \n".format(
+ dut,
+ metric,
+ st_rt,
+ )
)
return errormsg
@@ -2868,7 +3128,9 @@ def verify_rib(
for advertise_network_dict in advertise_network:
if "vrf" in advertise_network_dict:
- cmd = "{} vrf {} json".format(command, advertise_network_dict["vrf"])
+ cmd = "{} vrf {} json".format(
+ command, advertise_network_dict["vrf"]
+ )
else:
cmd = "{} json".format(command)
@@ -2947,6 +3209,7 @@ def verify_rib(
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return True
+
@retry(attempts=6, wait=2, return_is_str=True)
def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None):
"""
@@ -3327,7 +3590,12 @@ def verify_prefix_lists(tgen, input_dict):
for addr_type in prefix_lists_addr:
if not check_address_types(addr_type):
continue
-
+ # show ip prefix list
+ if addr_type == "ipv4":
+ cmd = "show ip prefix-list"
+ else:
+ cmd = "show {} prefix-list".format(addr_type)
+ show_prefix_list = run_frr_cmd(rnode, cmd)
for prefix_list in prefix_lists_addr[addr_type].keys():
if prefix_list in show_prefix_list:
errormsg = (
@@ -3550,7 +3818,6 @@ def verify_cli_json(tgen, input_dict):
"""
API to verify if JSON is available for clis
command.
-
Parameters
----------
* `tgen`: topogen object
@@ -3720,7 +3987,6 @@ def verify_vrf_vni(tgen, input_dict):
"""
API to verify vrf vni details using "show vrf vni json"
command.
-
Parameters
----------
* `tgen`: topogen object
@@ -3852,3 +4118,269 @@ def required_linux_kernel_version(required_version):
)
return error_msg
return True
+
+
+def iperfSendIGMPJoin(
+ tgen, server, bindToAddress, l4Type="UDP", join_interval=1, inc_step=0, repeat=0
+):
+ """
+ Run iperf to send IGMP join and traffic
+
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `l4Type`: string, one of [ TCP, UDP ]
+ * `server`: iperf server, from where IGMP join would be sent
+ * `bindToAddress`: bind to <host>, an interface or multicast
+ address
+ * `join_interval`: seconds between periodic bandwidth reports
+ * `inc_step`: increamental steps, by default 0
+ * `repeat`: Repetition of group, by default 0
+
+ returns:
+ --------
+ errormsg or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.routers()[server]
+
+ iperfArgs = "iperf -s "
+
+ # UDP/TCP
+ if l4Type == "UDP":
+ iperfArgs += "-u "
+
+ iperfCmd = iperfArgs
+ # Group address range to cover
+ if bindToAddress:
+ if type(bindToAddress) is not list:
+ Address = []
+ start = ipaddress.IPv4Address(frr_unicode(bindToAddress))
+
+ Address = [start]
+ next_ip = start
+
+ count = 1
+ while count < repeat:
+ next_ip += inc_step
+ Address.append(next_ip)
+ count += 1
+ bindToAddress = Address
+
+ for bindTo in bindToAddress:
+ iperfArgs = iperfCmd
+ iperfArgs += "-B %s " % bindTo
+
+ # Join interval
+ if join_interval:
+ iperfArgs += "-i %d " % join_interval
+
+ iperfArgs += " &>/dev/null &"
+ # Run iperf command to send IGMP join
+ logger.debug("[DUT: {}]: Running command: [{}]".format(server, iperfArgs))
+ output = rnode.run("set +m; {} sleep 0.5".format(iperfArgs))
+
+ # Check if iperf process is running
+ if output:
+ pid = output.split()[1]
+ rnode.run("touch /var/run/frr/iperf_server.pid")
+ rnode.run("echo %s >> /var/run/frr/iperf_server.pid" % pid)
+ else:
+ errormsg = "IGMP join is not sent for {}. Error: {}".format(bindTo, output)
+ logger.error(output)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def iperfSendTraffic(
+ tgen,
+ client,
+ bindToAddress,
+ ttl,
+ time=0,
+ l4Type="UDP",
+ inc_step=0,
+ repeat=0,
+ mappedAddress=None,
+):
+ """
+ Run iperf to send IGMP join and traffic
+
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `l4Type`: string, one of [ TCP, UDP ]
+ * `client`: iperf client, from where iperf traffic would be sent
+ * `bindToAddress`: bind to <host>, an interface or multicast
+ address
+ * `ttl`: time to live
+ * `time`: time in seconds to transmit for
+ * `inc_step`: increamental steps, by default 0
+ * `repeat`: Repetition of group, by default 0
+ * `mappedAddress`: Mapped Interface ip address
+
+ returns:
+ --------
+ errormsg or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.routers()[client]
+
+ iperfArgs = "iperf -c "
+
+ iperfCmd = iperfArgs
+ # Group address range to cover
+ if bindToAddress:
+ if type(bindToAddress) is not list:
+ Address = []
+ start = ipaddress.IPv4Address(frr_unicode(bindToAddress))
+
+ Address = [start]
+ next_ip = start
+
+ count = 1
+ while count < repeat:
+ next_ip += inc_step
+ Address.append(next_ip)
+ count += 1
+ bindToAddress = Address
+
+ for bindTo in bindToAddress:
+ iperfArgs = iperfCmd
+ iperfArgs += "%s " % bindTo
+
+ # Mapped Interface IP
+ if mappedAddress:
+ iperfArgs += "-B %s " % mappedAddress
+
+ # UDP/TCP
+ if l4Type == "UDP":
+ iperfArgs += "-u -b 0.012m "
+
+ # TTL
+ if ttl:
+ iperfArgs += "-T %d " % ttl
+
+ # Time
+ if time:
+ iperfArgs += "-t %d " % time
+
+ iperfArgs += " &>/dev/null &"
+
+ # Run iperf command to send multicast traffic
+ logger.debug("[DUT: {}]: Running command: [{}]".format(client, iperfArgs))
+ output = rnode.run("set +m; {} sleep 0.5".format(iperfArgs))
+
+ # Check if iperf process is running
+ if output:
+ pid = output.split()[1]
+ rnode.run("touch /var/run/frr/iperf_client.pid")
+ rnode.run("echo %s >> /var/run/frr/iperf_client.pid" % pid)
+ else:
+ errormsg = "Multicast traffic is not sent for {}. Error {}".format(
+ bindTo, output
+ )
+ logger.error(output)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def kill_iperf(tgen, dut=None, action=None):
+ """
+ Killing iperf process if running for any router in topology
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `dut` : Any iperf hostname to send igmp prune
+ * `action`: to kill igmp join iperf action is remove_join
+ to kill traffic iperf action is remove_traffic
+
+ Usage
+ ----
+ kill_iperf(tgen, dut ="i6", action="remove_join")
+
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for router, rnode in router_list.items():
+ # Run iperf command to send IGMP join
+ pid_client = rnode.run("cat /var/run/frr/iperf_client.pid")
+ pid_server = rnode.run("cat /var/run/frr/iperf_server.pid")
+ if action == "remove_join":
+ pids = pid_server
+ elif action == "remove_traffic":
+ pids = pid_client
+ else:
+ pids = "\n".join([pid_client, pid_server])
+ for pid in pids.split("\n"):
+ pid = pid.strip()
+ if pid.isdigit():
+ cmd = "set +m; kill -9 %s &> /dev/null" % pid
+ logger.debug("[DUT: {}]: Running command: [{}]".format(router, cmd))
+ rnode.run(cmd)
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+
+def verify_ip_nht(tgen, input_dict):
+ """
+ Running "show ip nht" command and verifying given nexthop resolution
+ Parameters
+ ----------
+ * `tgen` : topogen object
+ * `input_dict`: data to verify nexthop
+ Usage
+ -----
+ input_dict_4 = {
+ "r1": {
+ nh: {
+ "Address": nh,
+ "resolvedVia": "connected",
+ "nexthops": {
+ "nexthop1": {
+ "Interface": intf
+ }
+ }
+ }
+ }
+ }
+ result = verify_ip_nht(tgen, input_dict_4)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: verify_ip_nht()")
+
+ for router in input_dict.keys():
+ if router not in tgen.routers():
+ continue
+
+ rnode = tgen.routers()[router]
+ nh_list = input_dict[router]
+
+ if validate_ip_address(nh_list.keys()[0]) is "ipv6":
+ show_ip_nht = run_frr_cmd(rnode, "show ipv6 nht")
+ else:
+ show_ip_nht = run_frr_cmd(rnode, "show ip nht")
+
+ for nh in nh_list:
+ if nh in show_ip_nht:
+ logger.info("Nexthop %s is resolved on %s", nh, router)
+ return True
+ else:
+ errormsg = "Nexthop {} is resolved on {}".format(nh, router)
+ return errormsg
+
+ logger.debug("Exiting lib API: verify_ip_nht()")
+ return False