tests: [topojson]Enhance lib/common_config.py to support PIM automation

1. Enhanced lib/common_config.py for common configuration/verification needed
   for PIM automation
2. Ran it through (black) for expected formatting

Signed-off-by: kuldeepkash <kashyapk@vmware.com>
This commit is contained in:
kuldeepkash 2021-01-06 05:08:31 +00:00
parent e8cd26fdc5
commit aafca66993

View File

@ -31,13 +31,14 @@ from re import search as re_search
from tempfile import mkdtemp
import os
import io
import sys
import traceback
import socket
import ipaddress
import platform
if sys.version_info[0] > 2:
import io
import configparser
else:
import StringIO
@ -256,6 +257,7 @@ def create_common_configuration(
"bgp": "! BGP Config\n",
"vrf": "! VRF Config\n",
"ospf": "! OSPF Config\n",
"pim": "! PIM Config\n",
}
)
@ -388,6 +390,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)
@ -747,6 +751,13 @@ def start_topology(tgen, daemon=None):
router.load_config(
TopoRouter.RD_OSPF, "{}/{}/ospfd.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()
@ -834,9 +845,201 @@ def topo_daemons(tgen, topo):
if "ospf" in topo["routers"][rtr] and "ospfd" not in daemon_list:
daemon_list.append("ospfd")
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
#############################################
@ -1136,11 +1339,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]
@ -1292,7 +1495,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")
@ -1302,12 +1504,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:
@ -1319,11 +1519,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
@ -1365,6 +1565,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
#############################################
@ -2854,11 +3065,7 @@ 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
@ -2875,11 +3082,7 @@ 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
@ -3918,3 +4121,215 @@ 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))