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.py997
1 files changed, 597 insertions, 400 deletions
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index 81c7ba4d5c..1bce3c6bb2 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -18,55 +18,43 @@
# OF THIS SOFTWARE.
#
-from collections import OrderedDict
-from datetime import datetime, timedelta
-from time import sleep
-from copy import deepcopy
-from functools import wraps
-from re import search as re_search
-from tempfile import mkdtemp
-
+import ipaddress
import json
-import logging
import os
-import sys
-import traceback
+import platform
import socket
import subprocess
-import ipaddress
-import platform
-import pytest
+import sys
+import traceback
+from collections import OrderedDict
+from copy import deepcopy
+from datetime import datetime, timedelta
+from functools import wraps
+from re import search as re_search
+from time import sleep
try:
# Imports from python2
- from StringIO import StringIO
import ConfigParser as configparser
except ImportError:
# Imports from python3
- from io import StringIO
import configparser
-from lib.topolog import logger, logger_config
+from lib.micronet import comm_error
from lib.topogen import TopoRouter, get_topogen
-from lib.topotest import interface_set_status, version_cmp, frr_unicode
+from lib.topolog import get_logger, logger
+from lib.topotest import frr_unicode, interface_set_status, version_cmp
+from lib import topotest
FRRCFG_FILE = "frr_json.conf"
FRRCFG_BKUP_FILE = "frr_json_initial.conf"
ERROR_LIST = ["Malformed", "Failure", "Unknown", "Incomplete"]
-ROUTER_LIST = []
####
CD = os.path.dirname(os.path.realpath(__file__))
PYTESTINI_PATH = os.path.join(CD, "../pytest.ini")
-# Creating tmp dir with testsuite name to avoid conflict condition when
-# multiple testsuites run together. All temporary files would be created
-# in this dir and this dir would be removed once testsuite run is
-# completed
-LOGDIR = "/tmp/topotests/"
-TMPDIR = None
-
# NOTE: to save execution logs to log file frrtest_log_dir must be configured
# in `pytest.ini`.
config = configparser.ConfigParser()
@@ -138,17 +126,22 @@ DEBUG_LOGS = {
],
}
+g_iperf_client_procs = {}
+g_iperf_server_procs = {}
+
+
def is_string(value):
try:
return isinstance(value, basestring)
except NameError:
return isinstance(value, str)
+
if config.has_option("topogen", "verbosity"):
loglevel = config.get("topogen", "verbosity")
- loglevel = loglevel.upper()
+ loglevel = loglevel.lower()
else:
- loglevel = "INFO"
+ loglevel = "info"
if config.has_option("topogen", "frrtest_log_dir"):
frrtest_log_dir = config.get("topogen", "frrtest_log_dir")
@@ -157,8 +150,8 @@ if config.has_option("topogen", "frrtest_log_dir"):
frrtest_log_file = frrtest_log_dir + logfile_name + str(time_stamp)
print("frrtest_log_file..", frrtest_log_file)
- logger = logger_config.get_logger(
- name="test_execution_logs", log_level=loglevel, target=frrtest_log_file
+ logger = get_logger(
+ "test_execution_logs", log_level=loglevel, target=frrtest_log_file
)
print("Logs will be sent to logfile: {}".format(frrtest_log_file))
@@ -218,8 +211,6 @@ def set_seq_id(obj_type, router, id, obj_name):
class InvalidCLIError(Exception):
"""Raise when the CLI command is wrong"""
- pass
-
def run_frr_cmd(rnode, cmd, isjson=False):
"""
@@ -284,7 +275,7 @@ def apply_raw_config(tgen, input_dict):
if not isinstance(config_cmd, list):
config_cmd = [config_cmd]
- frr_cfg_file = "{}/{}/{}".format(TMPDIR, router_name, FRRCFG_FILE)
+ frr_cfg_file = "{}/{}/{}".format(tgen.logdir, router_name, FRRCFG_FILE)
with open(frr_cfg_file, "w") as cfg:
for cmd in config_cmd:
cfg.write("{}\n".format(cmd))
@@ -314,7 +305,6 @@ def create_common_configurations(
-------
True or False
"""
- TMPDIR = os.path.join(LOGDIR, tgen.modname)
config_map = OrderedDict(
{
@@ -342,7 +332,7 @@ def create_common_configurations(
routers = config_dict.keys()
for router in routers:
- fname = "{}/{}/{}".format(TMPDIR, router, FRRCFG_FILE)
+ fname = "{}/{}/{}".format(tgen.logdir, router, FRRCFG_FILE)
try:
frr_cfg_fd = open(fname, mode)
if config_type:
@@ -352,9 +342,7 @@ def create_common_configurations(
frr_cfg_fd.write("\n")
except IOError as err:
- logger.error(
- "Unable to open FRR Config '%s': %s" % (fname, str(err))
- )
+ logger.error("Unable to open FRR Config '%s': %s" % (fname, str(err)))
return False
finally:
frr_cfg_fd.close()
@@ -483,6 +471,40 @@ def check_router_status(tgen):
return True
+def save_initial_config_on_routers(tgen):
+ """Save current configuration on routers to FRRCFG_BKUP_FILE.
+
+ FRRCFG_BKUP_FILE is the file that will be restored when `reset_config_on_routers()`
+ is called.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ """
+ router_list = tgen.routers()
+ target_cfg_fmt = tgen.logdir + "/{}/frr_json_initial.conf"
+
+ # Get all running configs in parallel
+ procs = {}
+ for rname in router_list:
+ logger.info("Fetching running config for router %s", rname)
+ procs[rname] = router_list[rname].popen(
+ ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"],
+ stdin=None,
+ stdout=open(target_cfg_fmt.format(rname), "w"),
+ stderr=subprocess.PIPE,
+ )
+ for rname, p in procs.items():
+ _, error = p.communicate()
+ if p.returncode:
+ logger.error(
+ "Get running config for %s failed %d: %s", rname, p.returncode, error
+ )
+ raise InvalidCLIError(
+ "vtysh show running error on {}: {}".format(rname, error)
+ )
+
+
def reset_config_on_routers(tgen, routerName=None):
"""
Resets configuration on routers to the snapshot created using input JSON
@@ -496,17 +518,25 @@ def reset_config_on_routers(tgen, routerName=None):
logger.debug("Entering API: reset_config_on_routers")
+ tgen.cfg_gen += 1
+ gen = tgen.cfg_gen
+
# Trim the router list if needed
router_list = tgen.routers()
if routerName:
- if ((routerName not in ROUTER_LIST) or (routerName not in router_list)):
- logger.debug("Exiting API: reset_config_on_routers: no routers")
+ if routerName not in router_list:
+ logger.warning(
+ "Exiting API: reset_config_on_routers: no router %s",
+ routerName,
+ exc_info=True,
+ )
return True
- router_list = { routerName: router_list[routerName] }
+ router_list = {routerName: router_list[routerName]}
- delta_fmt = TMPDIR + "/{}/delta.conf"
- init_cfg_fmt = TMPDIR + "/{}/frr_json_initial.conf"
- run_cfg_fmt = TMPDIR + "/{}/frr.sav"
+ delta_fmt = tgen.logdir + "/{}/delta-{}.conf"
+ # FRRCFG_BKUP_FILE
+ target_cfg_fmt = tgen.logdir + "/{}/frr_json_initial.conf"
+ run_cfg_fmt = tgen.logdir + "/{}/frr-{}.sav"
#
# Get all running configs in parallel
@@ -517,36 +547,46 @@ def reset_config_on_routers(tgen, routerName=None):
procs[rname] = router_list[rname].popen(
["/usr/bin/env", "vtysh", "-c", "show running-config no-header"],
stdin=None,
- stdout=open(run_cfg_fmt.format(rname), "w"),
+ stdout=open(run_cfg_fmt.format(rname, gen), "w"),
stderr=subprocess.PIPE,
)
for rname, p in procs.items():
_, error = p.communicate()
if p.returncode:
- logger.error("Get running config for %s failed %d: %s", rname, p.returncode, error)
- raise InvalidCLIError("vtysh show running error on {}: {}".format(rname, error))
+ logger.error(
+ "Get running config for %s failed %d: %s", rname, p.returncode, error
+ )
+ raise InvalidCLIError(
+ "vtysh show running error on {}: {}".format(rname, error)
+ )
#
# Get all delta's in parallel
#
procs = {}
for rname in router_list:
- logger.info("Generating delta for router %s to new configuration", rname)
- procs[rname] = subprocess.Popen(
- [ "/usr/lib/frr/frr-reload.py",
- "--test-reset",
- "--input",
- run_cfg_fmt.format(rname),
- "--test",
- init_cfg_fmt.format(rname) ],
+ logger.info(
+ "Generating delta for router %s to new configuration (gen %d)", rname, gen
+ )
+ procs[rname] = tgen.net.popen(
+ [
+ "/usr/lib/frr/frr-reload.py",
+ "--test-reset",
+ "--input",
+ run_cfg_fmt.format(rname, gen),
+ "--test",
+ target_cfg_fmt.format(rname),
+ ],
stdin=None,
- stdout=open(delta_fmt.format(rname), "w"),
+ stdout=open(delta_fmt.format(rname, gen), "w"),
stderr=subprocess.PIPE,
)
for rname, p in procs.items():
_, error = p.communicate()
if p.returncode:
- logger.error("Delta file creation for %s failed %d: %s", rname, p.returncode, error)
+ logger.error(
+ "Delta file creation for %s failed %d: %s", rname, p.returncode, error
+ )
raise InvalidCLIError("frr-reload error for {}: {}".format(rname, error))
#
@@ -557,23 +597,29 @@ def reset_config_on_routers(tgen, routerName=None):
logger.info("Applying delta config on router %s", rname)
procs[rname] = router_list[rname].popen(
- ["/usr/bin/env", "vtysh", "-f", delta_fmt.format(rname)],
+ ["/usr/bin/env", "vtysh", "-f", delta_fmt.format(rname, gen)],
stdin=None,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
for rname, p in procs.items():
output, _ = p.communicate()
- vtysh_command = "vtysh -f {}".format(delta_fmt.format(rname))
+ vtysh_command = "vtysh -f {}".format(delta_fmt.format(rname, gen))
if not p.returncode:
router_list[rname].logger.info(
- '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output)
+ '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(
+ vtysh_command, output
+ )
)
else:
router_list[rname].logger.warning(
- '\nvtysh config apply failed => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output)
+ '\nvtysh config apply failed => "{}"\nvtysh output <= "{}"'.format(
+ vtysh_command, output
+ )
+ )
+ logger.error(
+ "Delta file apply for %s failed %d: %s", rname, p.returncode, output
)
- logger.error("Delta file apply for %s failed %d: %s", rname, p.returncode, output)
# We really need to enable this failure; however, currently frr-reload.py
# producing invalid "no" commands as it just preprends "no", but some of the
@@ -599,14 +645,59 @@ def reset_config_on_routers(tgen, routerName=None):
for rname, p in procs.items():
output, _ = p.communicate()
if p.returncode:
- logger.warning("Get running config for %s failed %d: %s", rname, p.returncode, output)
+ logger.warning(
+ "Get running config for %s failed %d: %s",
+ rname,
+ p.returncode,
+ output,
+ )
else:
- logger.info("Configuration on router %s after reset:\n%s", rname, output)
+ logger.info(
+ "Configuration on router %s after reset:\n%s", rname, output
+ )
logger.debug("Exiting API: reset_config_on_routers")
return True
+def prep_load_config_to_routers(tgen, *config_name_list):
+ """Create common config for `load_config_to_routers`.
+
+ The common config file is constructed from the list of sub-config files passed as
+ position arguments to this function. Each entry in `config_name_list` is looked for
+ under the router sub-directory in the test directory and those files are
+ concatenated together to create the common config. e.g.,
+
+ # Routers are "r1" and "r2", test file is `example/test_example_foo.py`
+ prepare_load_config_to_routers(tgen, "bgpd.conf", "ospfd.conf")
+
+ When the above call is made the files in
+
+ example/r1/bgpd.conf
+ example/r1/ospfd.conf
+
+ Are concat'd together into a single config file that will be loaded on r1, and
+
+ example/r2/bgpd.conf
+ example/r2/ospfd.conf
+
+ Are concat'd together into a single config file that will be loaded on r2 when
+ the call to `load_config_to_routers` is made.
+ """
+
+ routers = tgen.routers()
+ for rname, router in routers.items():
+ destname = "{}/{}/{}".format(tgen.logdir, rname, FRRCFG_FILE)
+ wmode = "w"
+ for cfbase in config_name_list:
+ script_dir = os.environ["PYTEST_TOPOTEST_SCRIPTDIR"]
+ confname = os.path.join(script_dir, "{}/{}".format(rname, cfbase))
+ with open(confname, "r") as cf:
+ with open(destname, wmode) as df:
+ df.write(cf.read())
+ wmode = "a"
+
+
def load_config_to_routers(tgen, routers, save_bkup=False):
"""
Loads configuration on routers from the file FRRCFG_FILE.
@@ -623,28 +714,38 @@ def load_config_to_routers(tgen, routers, save_bkup=False):
logger.debug("Entering API: load_config_to_routers")
+ tgen.cfg_gen += 1
+ gen = tgen.cfg_gen
+
base_router_list = tgen.routers()
router_list = {}
for router in routers:
- if (router not in ROUTER_LIST) or (router not in base_router_list):
+ if router not in base_router_list:
continue
router_list[router] = base_router_list[router]
- frr_cfg_file_fmt = TMPDIR + "/{}/" + FRRCFG_FILE
- frr_cfg_bkup_fmt = TMPDIR + "/{}/" + FRRCFG_BKUP_FILE
+ frr_cfg_file_fmt = tgen.logdir + "/{}/" + FRRCFG_FILE
+ frr_cfg_save_file_fmt = tgen.logdir + "/{}/{}-" + FRRCFG_FILE
+ frr_cfg_bkup_fmt = tgen.logdir + "/{}/" + FRRCFG_BKUP_FILE
procs = {}
for rname in router_list:
router = router_list[rname]
try:
frr_cfg_file = frr_cfg_file_fmt.format(rname)
- frr_cfg_bkup = frr_cfg_bkup_fmt.format(rname)
+ frr_cfg_save_file = frr_cfg_save_file_fmt.format(rname, gen)
+ frr_cfg_bkup = frr_cfg_bkup_fmt.format(rname)
with open(frr_cfg_file, "r+") as cfg:
data = cfg.read()
logger.info(
- "Applying following configuration on router"
- " {}:\n{}".format(rname, data)
+ "Applying following configuration on router %s (gen: %d):\n%s",
+ rname,
+ gen,
+ data,
)
+ # Always save a copy of what we just did
+ with open(frr_cfg_save_file, "w") as bkup:
+ bkup.write(data)
if save_bkup:
with open(frr_cfg_bkup, "w") as bkup:
bkup.write(data)
@@ -655,13 +756,12 @@ def load_config_to_routers(tgen, routers, save_bkup=False):
stderr=subprocess.STDOUT,
)
except IOError as err:
- logging.error(
- "Unable to open config File. error(%s): %s",
- err.errno, err.strerror
+ logger.error(
+ "Unable to open config File. error(%s): %s", err.errno, err.strerror
)
return False
except Exception as error:
- logging.error("Unable to apply config on %s: %s", rname, str(error))
+ logger.error("Unable to apply config on %s: %s", rname, str(error))
return False
errors = []
@@ -671,15 +771,25 @@ def load_config_to_routers(tgen, routers, save_bkup=False):
vtysh_command = "vtysh -f " + frr_cfg_file
if not p.returncode:
router_list[rname].logger.info(
- '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output)
+ '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(
+ vtysh_command, output
+ )
)
else:
router_list[rname].logger.error(
- '\nvtysh config apply failed => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output)
+ '\nvtysh config apply failed => "{}"\nvtysh output <= "{}"'.format(
+ vtysh_command, output
+ )
+ )
+ logger.error(
+ "Config apply for %s failed %d: %s", rname, p.returncode, output
)
- logger.error("Config apply for %s failed %d: %s", rname, p.returncode, output)
# We can't thorw an exception here as we won't clear the config file.
- errors.append(InvalidCLIError("load_config_to_routers error for {}: {}".format(rname, output)))
+ errors.append(
+ InvalidCLIError(
+ "load_config_to_routers error for {}: {}".format(rname, output)
+ )
+ )
# Empty the config file or we append to it next time through.
with open(frr_cfg_file, "r+") as cfg:
@@ -699,9 +809,14 @@ def load_config_to_routers(tgen, routers, save_bkup=False):
for rname, p in procs.items():
output, _ = p.communicate()
if p.returncode:
- logger.warning("Get running config for %s failed %d: %s", rname, p.returncode, output)
+ logger.warning(
+ "Get running config for %s failed %d: %s",
+ rname,
+ p.returncode,
+ output,
+ )
else:
- logger.info("New configuration for router %s:\n%s", rname,output)
+ logger.info("New configuration for router %s:\n%s", rname, output)
logger.debug("Exiting API: load_config_to_routers")
return not errors
@@ -720,6 +835,21 @@ def load_config_to_router(tgen, routerName, save_bkup=False):
return load_config_to_routers(tgen, [routerName], save_bkup)
+def reset_with_new_configs(tgen, *cflist):
+ """Reset the router to initial config, then load new configs.
+
+ Resets routers to the initial config state (see `save_initial_config_on_routers()
+ and `reset_config_on_routers()` `), then concat list of router sub-configs together
+ and load onto the routers (see `prep_load_config_to_routers()` and
+ `load_config_to_routers()`)
+ """
+ routers = tgen.routers()
+
+ reset_config_on_routers(tgen)
+ prep_load_config_to_routers(tgen, *cflist)
+ load_config_to_routers(tgen, tgen.routers(), save_bkup=False)
+
+
def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None):
"""
API to get the link local ipv6 address of a particular interface using
@@ -758,37 +888,38 @@ def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None):
else:
cmd = "show interface"
for chk_ll in range(0, 60):
- sleep(1/4)
+ sleep(1 / 4)
ifaces = router_list[router].run('vtysh -c "{}"'.format(cmd))
# Fix newlines (make them all the same)
- ifaces = ('\n'.join(ifaces.splitlines()) + '\n').splitlines()
+ ifaces = ("\n".join(ifaces.splitlines()) + "\n").splitlines()
interface = None
ll_per_if_count = 0
for line in ifaces:
# Interface name
- m = re_search('Interface ([a-zA-Z0-9-]+) is', line)
+ m = re_search("Interface ([a-zA-Z0-9-]+) is", line)
if m:
interface = m.group(1).split(" ")[0]
ll_per_if_count = 0
# Interface ip
- m1 = re_search('inet6 (fe80[:a-fA-F0-9]+[\/0-9]+)',
- line)
+ m1 = re_search("inet6 (fe80[:a-fA-F0-9]+/[0-9]+)", line)
if m1:
local = m1.group(1)
ll_per_if_count += 1
if ll_per_if_count > 1:
- linklocal += [["%s-%s" %
- (interface, ll_per_if_count), local]]
+ linklocal += [["%s-%s" % (interface, ll_per_if_count), local]]
else:
linklocal += [[interface, local]]
try:
if linklocal:
if intf:
- return [_linklocal[1] for _linklocal in linklocal if _linklocal[0]==intf][0].\
- split("/")[0]
+ return [
+ _linklocal[1]
+ for _linklocal in linklocal
+ if _linklocal[0] == intf
+ ][0].split("/")[0]
return linklocal
except IndexError:
continue
@@ -806,28 +937,23 @@ def generate_support_bundle():
tgen = get_topogen()
router_list = tgen.routers()
- test_name = os.environ.get('PYTEST_CURRENT_TEST').split(':')[-1].split(' ')[0]
-
- TMPDIR = os.path.join(LOGDIR, tgen.modname)
+ test_name = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0]
bundle_procs = {}
for rname, rnode in router_list.items():
logger.info("Spawn collection of support bundle for %s", rname)
- rnode.run("mkdir -p /var/log/frr")
- bundle_procs[rname] = tgen.net[rname].popen(
+ dst_bundle = "{}/{}/support_bundles/{}".format(tgen.logdir, rname, test_name)
+ rnode.run("mkdir -p " + dst_bundle)
+
+ gen_sup_cmd = [
"/usr/lib/frr/generate_support_bundle.py",
- stdin=None,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- )
+ "--log-dir=" + dst_bundle,
+ ]
+ bundle_procs[rname] = tgen.net[rname].popen(gen_sup_cmd, stdin=None)
for rname, rnode in router_list.items():
- dst_bundle = "{}/{}/support_bundles/{}".format(TMPDIR, rname, test_name)
- src_bundle = "/var/log/frr"
-
+ logger.info("Waiting on support bundle for %s", rname)
output, error = bundle_procs[rname].communicate()
-
- logger.info("Saving support bundle for %s", rname)
if output:
logger.info(
"Output from collecting support bundle for %s:\n%s", rname, output
@@ -836,9 +962,6 @@ def generate_support_bundle():
logger.warning(
"Error from collecting support bundle for %s:\n%s", rname, error
)
- rnode.run("rm -rf {}".format(dst_bundle))
- rnode.run("mkdir -p {}".format(dst_bundle))
- rnode.run("mv -f {}/* {}".format(src_bundle, dst_bundle))
return True
@@ -850,21 +973,19 @@ def start_topology(tgen, daemon=None):
* `tgen` : topogen object
"""
- global TMPDIR, ROUTER_LIST
# Starting topology
tgen.start_topology()
# Starting daemons
router_list = tgen.routers()
- ROUTER_LIST = sorted(
+ routers_sorted = sorted(
router_list.keys(), key=lambda x: int(re_search("[0-9]+", x).group(0))
)
- TMPDIR = os.path.join(LOGDIR, tgen.modname)
linux_ver = ""
router_list = tgen.routers()
- for rname in ROUTER_LIST:
+ for rname in routers_sorted:
router = router_list[rname]
# It will help in debugging the failures, will give more details on which
@@ -874,49 +995,51 @@ def start_topology(tgen, daemon=None):
logger.info("Logging platform related details: \n %s \n", linux_ver)
try:
- os.chdir(TMPDIR)
-
- # Creating router named dir and empty zebra.conf bgpd.conf files
- # inside the current directory
- if os.path.isdir("{}".format(rname)):
- os.system("rm -rf {}".format(rname))
- os.mkdir("{}".format(rname))
- os.system("chmod -R go+rw {}".format(rname))
- os.chdir("{}/{}".format(TMPDIR, rname))
- os.system("touch zebra.conf bgpd.conf")
- else:
- os.mkdir("{}".format(rname))
- os.system("chmod -R go+rw {}".format(rname))
- os.chdir("{}/{}".format(TMPDIR, rname))
- os.system("touch zebra.conf bgpd.conf")
+ os.chdir(tgen.logdir)
+
+ # # Creating router named dir and empty zebra.conf bgpd.conf files
+ # # inside the current directory
+ # if os.path.isdir("{}".format(rname)):
+ # os.system("rm -rf {}".format(rname))
+ # os.mkdir("{}".format(rname))
+ # os.system("chmod -R go+rw {}".format(rname))
+ # os.chdir("{}/{}".format(tgen.logdir, rname))
+ # os.system("touch zebra.conf bgpd.conf")
+ # else:
+ # os.mkdir("{}".format(rname))
+ # os.system("chmod -R go+rw {}".format(rname))
+ # os.chdir("{}/{}".format(tgen.logdir, rname))
+ # os.system("touch zebra.conf bgpd.conf")
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 daemon
router.load_config(
- TopoRouter.RD_ZEBRA, "{}/{}/zebra.conf".format(TMPDIR, rname)
+ TopoRouter.RD_ZEBRA, "{}/{}/zebra.conf".format(tgen.logdir, rname)
)
# Loading empty bgpd.conf file to router, to start the bgp daemon
- router.load_config(TopoRouter.RD_BGP, "{}/{}/bgpd.conf".format(TMPDIR, rname))
+ router.load_config(
+ TopoRouter.RD_BGP, "{}/{}/bgpd.conf".format(tgen.logdir, rname)
+ )
if daemon and "ospfd" in daemon:
# Loading empty ospf.conf file to router, to start the bgp daemon
router.load_config(
- TopoRouter.RD_OSPF, "{}/{}/ospfd.conf".format(TMPDIR, rname)
+ TopoRouter.RD_OSPF, "{}/{}/ospfd.conf".format(tgen.logdir, rname)
)
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)
+ TopoRouter.RD_OSPF6, "{}/{}/ospf6d.conf".format(tgen.logdir, 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)
+ TopoRouter.RD_PIM, "{}/{}/pimd.conf".format(tgen.logdir, rname)
)
# Starting routers
@@ -991,18 +1114,21 @@ def number_to_column(routerName):
return ord(routerName[0]) - 97
-def topo_daemons(tgen, topo):
+def topo_daemons(tgen, topo=None):
"""
Returns daemon list required for the suite based on topojson.
"""
daemon_list = []
+ if topo is None:
+ topo = tgen.json_topo
+
router_list = tgen.routers()
- ROUTER_LIST = sorted(
+ routers_sorted = sorted(
router_list.keys(), key=lambda x: int(re_search("[0-9]+", x).group(0))
)
- for rtr in ROUTER_LIST:
+ for rtr in routers_sorted:
if "ospf" in topo["routers"][rtr] and "ospfd" not in daemon_list:
daemon_list.append("ospfd")
@@ -1047,7 +1173,7 @@ def add_interfaces_to_vlan(tgen, input_dict):
router_list = tgen.routers()
for dut in input_dict.keys():
- rnode = tgen.routers()[dut]
+ rnode = router_list[dut]
if "vlan" in input_dict[dut]:
for vlan, interfaces in input_dict[dut]["vlan"].items():
@@ -1056,9 +1182,7 @@ def add_interfaces_to_vlan(tgen, input_dict):
# Adding interface to VLAN
vlan_intf = "{}.{}".format(interface, vlan)
cmd = "ip link add link {} name {} type vlan id {}".format(
- interface,
- vlan_intf,
- vlan
+ interface, vlan_intf, vlan
)
logger.info("[DUT: %s]: Running command: %s", dut, cmd)
rnode.run(cmd)
@@ -1071,8 +1195,7 @@ def add_interfaces_to_vlan(tgen, input_dict):
# Assigning IP address
ifaddr = ipaddress.ip_interface(
u"{}/{}".format(
- frr_unicode(data["ip"]),
- frr_unicode(data["subnet"])
+ frr_unicode(data["ip"]), frr_unicode(data["subnet"])
)
)
@@ -1123,7 +1246,7 @@ def tcpdump_capture_start(
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
- rnode = tgen.routers()[router]
+ rnode = tgen.gears[router]
if timeout > 0:
cmd = "timeout {}".format(timeout)
@@ -1140,7 +1263,7 @@ def tcpdump_capture_start(
cmdargs += " -s 0 {}".format(str(options))
if cap_file:
- file_name = os.path.join(LOGDIR, tgen.modname, router, cap_file)
+ file_name = os.path.join(tgen.logdir, router, cap_file)
cmdargs += " -w {}".format(str(file_name))
# Remove existing capture file
rnode.run("rm -rf {}".format(file_name))
@@ -1152,7 +1275,9 @@ def tcpdump_capture_start(
if not background:
rnode.run(cmdargs)
else:
- rnode.run("nohup {} & /dev/null 2>&1".format(cmdargs))
+ # XXX this & is bogus doesn't work
+ # rnode.run("nohup {} & /dev/null 2>&1".format(cmdargs))
+ rnode.run("nohup {} > /dev/null 2>&1".format(cmdargs))
# Check if tcpdump process is running
if background:
@@ -1199,7 +1324,7 @@ def tcpdump_capture_stop(tgen, router):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
- rnode = tgen.routers()[router]
+ rnode = tgen.gears[router]
# Check if tcpdump process is running
result = rnode.run("ps -ef | grep tcpdump")
@@ -1209,6 +1334,7 @@ def tcpdump_capture_stop(tgen, router):
errormsg = "tcpdump is not running {}".format("tcpdump")
return errormsg
else:
+ # XXX this doesn't work with micronet
ppid = tgen.net.nameToNode[rnode.name].pid
rnode.run("set +m; pkill -P %s tcpdump &> /dev/null" % ppid)
logger.info("Stopped tcpdump capture")
@@ -1268,7 +1394,7 @@ def create_debug_log_config(tgen, input_dict, build=False):
log_file = debug_dict.setdefault("log_file", None)
if log_file:
- _log_file = os.path.join(LOGDIR, tgen.modname, log_file)
+ _log_file = os.path.join(tgen.logdir, log_file)
debug_config.append("log file {} \n".format(_log_file))
if type(enable_logs) is list:
@@ -1374,9 +1500,8 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False):
config_data_dict = {}
for c_router, c_data in input_dict.items():
- rnode = tgen.routers()[c_router]
+ rnode = tgen.gears[c_router]
config_data = []
-
if "vrfs" in c_data:
for vrf in c_data["vrfs"]:
del_action = vrf.setdefault("delete", False)
@@ -1490,7 +1615,7 @@ def create_interface_in_kernel(
to create
"""
- rnode = tgen.routers()[dut]
+ rnode = tgen.gears[dut]
if create:
cmd = "ip link show {0} >/dev/null || ip link add {0} type dummy".format(name)
@@ -1499,10 +1624,9 @@ def create_interface_in_kernel(
if not netmask:
ifaddr = ipaddress.ip_interface(frr_unicode(ip_addr))
else:
- ifaddr = ipaddress.ip_interface(u"{}/{}".format(
- frr_unicode(ip_addr),
- frr_unicode(netmask)
- ))
+ ifaddr = ipaddress.ip_interface(
+ u"{}/{}".format(frr_unicode(ip_addr), frr_unicode(netmask))
+ )
cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format(
ifaddr.version, name, ifaddr
)
@@ -1528,7 +1652,7 @@ def shutdown_bringup_interface_in_kernel(tgen, dut, intf_name, ifaceaction=False
ineterface
"""
- rnode = tgen.routers()[dut]
+ rnode = tgen.gears[dut]
cmd = "ip link set dev"
if ifaceaction:
@@ -1737,7 +1861,7 @@ def interface_status(tgen, topo, input_dict):
interface_list = input_dict[router]["interface_list"]
status = input_dict[router].setdefault("status", "up")
for intf in interface_list:
- rnode = tgen.routers()[router]
+ rnode = tgen.gears[router]
interface_set_status(rnode, intf, status)
rlist.append(router)
@@ -1786,7 +1910,9 @@ def retry(retry_timeout, initial_wait=0, expected=True, diag_pct=0.75):
_diag_pct = kwargs.pop("diag_pct", diag_pct)
start_time = datetime.now()
- retry_until = datetime.now() + timedelta(seconds=_retry_timeout + _initial_wait)
+ retry_until = datetime.now() + timedelta(
+ seconds=_retry_timeout + _initial_wait
+ )
if initial_wait > 0:
logger.info("Waiting for [%s]s as initial delay", initial_wait)
@@ -1807,10 +1933,13 @@ def retry(retry_timeout, initial_wait=0, expected=True, diag_pct=0.75):
# Positive result, but happened after timeout failure, very important to
# note for fixing tests.
- logger.warning("RETRY DIAGNOSTIC: SUCCEED after FAILED with requested timeout of %.1fs; however, succeeded in %.1fs, investigate timeout timing",
- _retry_timeout, (datetime.now() - start_time).total_seconds())
+ logger.warning(
+ "RETRY DIAGNOSTIC: SUCCEED after FAILED with requested timeout of %.1fs; however, succeeded in %.1fs, investigate timeout timing",
+ _retry_timeout,
+ (datetime.now() - start_time).total_seconds(),
+ )
if isinstance(saved_failure, Exception):
- raise saved_failure # pylint: disable=E0702
+ raise saved_failure # pylint: disable=E0702
return saved_failure
except Exception as error:
@@ -1818,16 +1947,20 @@ def retry(retry_timeout, initial_wait=0, expected=True, diag_pct=0.75):
ret = error
if seconds_left < 0 and saved_failure:
- logger.info("RETRY DIAGNOSTIC: Retry timeout reached, still failing")
+ logger.info(
+ "RETRY DIAGNOSTIC: Retry timeout reached, still failing"
+ )
if isinstance(saved_failure, Exception):
- raise saved_failure # pylint: disable=E0702
+ raise saved_failure # pylint: disable=E0702
return saved_failure
if seconds_left < 0:
logger.info("Retry timeout of %ds reached", _retry_timeout)
saved_failure = ret
- retry_extra_delta = timedelta(seconds=seconds_left + _retry_timeout * _diag_pct)
+ retry_extra_delta = timedelta(
+ seconds=seconds_left + _retry_timeout * _diag_pct
+ )
retry_until = datetime.now() + retry_extra_delta
seconds_left = retry_extra_delta.total_seconds()
@@ -1841,11 +1974,17 @@ def retry(retry_timeout, initial_wait=0, expected=True, diag_pct=0.75):
return saved_failure
if saved_failure:
- logger.info("RETRY DIAG: [failure] Sleeping %ds until next retry with %.1f retry time left - too see if timeout was too short",
- retry_sleep, seconds_left)
+ logger.info(
+ "RETRY DIAG: [failure] Sleeping %ds until next retry with %.1f retry time left - too see if timeout was too short",
+ retry_sleep,
+ seconds_left,
+ )
else:
- logger.info("Sleeping %ds until next retry with %.1f retry time left",
- retry_sleep, seconds_left)
+ logger.info(
+ "Sleeping %ds until next retry with %.1f retry time left",
+ retry_sleep,
+ seconds_left,
+ )
sleep(retry_sleep)
func_retry._original = func
@@ -1969,12 +2108,13 @@ def create_interfaces_cfg(tgen, topo, build=False):
interface_data.append("ipv6 address {}".format(intf_addr))
# Wait for vrf interfaces to get link local address once they are up
- if not destRouterLink == 'lo' and 'vrf' in topo[c_router][
- 'links'][destRouterLink]:
- vrf = topo[c_router]['links'][destRouterLink]['vrf']
- intf = topo[c_router]['links'][destRouterLink]['interface']
- ll = get_frr_ipv6_linklocal(tgen, c_router, intf=intf,
- vrf = vrf)
+ if (
+ not destRouterLink == "lo"
+ and "vrf" in topo[c_router]["links"][destRouterLink]
+ ):
+ vrf = topo[c_router]["links"][destRouterLink]["vrf"]
+ intf = topo[c_router]["links"][destRouterLink]["interface"]
+ ll = get_frr_ipv6_linklocal(tgen, c_router, intf=intf, vrf=vrf)
if "ipv6-link-local" in data:
intf_addr = c_data["links"][destRouterLink]["ipv6-link-local"]
@@ -2797,7 +2937,7 @@ def addKernelRoute(
logger.debug("Entering lib API: addKernelRoute()")
- rnode = tgen.routers()[router]
+ rnode = tgen.gears[router]
if type(group_addr_range) is not list:
group_addr_range = [group_addr_range]
@@ -2832,6 +2972,8 @@ def addKernelRoute(
ip, mask = grp_addr.split("/")
if mask == "32" or mask == "128":
grp_addr = ip
+ else:
+ mask = "32" if addr_type == "ipv4" else "128"
if not re_search(r"{}".format(grp_addr), result) and mask != "0":
errormsg = (
@@ -2879,7 +3021,7 @@ def configure_vxlan(tgen, input_dict):
router_list = tgen.routers()
for dut in input_dict.keys():
- rnode = tgen.routers()[dut]
+ rnode = router_list[dut]
if "vxlan" in input_dict[dut]:
for vxlan_dict in input_dict[dut]["vxlan"]:
@@ -2978,7 +3120,7 @@ def configure_brctl(tgen, topo, input_dict):
router_list = tgen.routers()
for dut in input_dict.keys():
- rnode = tgen.routers()[dut]
+ rnode = router_list[dut]
if "brctl" in input_dict[dut]:
for brctl_dict in input_dict[dut]["brctl"]:
@@ -3064,7 +3206,7 @@ def configure_interface_mac(tgen, input_dict):
router_list = tgen.routers()
for dut in input_dict.keys():
- rnode = tgen.routers()[dut]
+ rnode = router_list[dut]
for intf, mac in input_dict[dut].items():
cmd = "ip link set {} address {}".format(intf, mac)
@@ -3535,7 +3677,11 @@ def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
router_list = tgen.routers()
+ if dut not in router_list:
+ return
+
for routerInput in input_dict.keys():
+ # XXX replace with router = dut; rnode = router_list[dut]
for router, rnode in router_list.items():
if router != dut:
continue
@@ -3780,11 +3926,11 @@ def verify_admin_distance_for_static_routes(tgen, input_dict):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ router_list = tgen.routers()
for router in input_dict.keys():
- if router not in tgen.routers():
+ if router not in router_list:
continue
-
- rnode = tgen.routers()[router]
+ rnode = router_list[router]
for static_route in input_dict[router]["static_routes"]:
addr_type = validate_ip_address(static_route["network"])
@@ -3862,11 +4008,12 @@ def verify_prefix_lists(tgen, input_dict):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ router_list = tgen.routers()
for router in input_dict.keys():
- if router not in tgen.routers():
+ if router not in router_list:
continue
- rnode = tgen.routers()[router]
+ rnode = router_list[router]
# Show ip prefix list
show_prefix_list = run_frr_cmd(rnode, "show ip prefix-list")
@@ -3925,11 +4072,12 @@ def verify_route_maps(tgen, input_dict):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ router_list = tgen.routers()
for router in input_dict.keys():
- if router not in tgen.routers():
+ if router not in router_list:
continue
- rnode = tgen.routers()[router]
+ rnode = router_list[router]
# Show ip route-map
show_route_maps = rnode.vtysh_cmd("show route-map")
@@ -3978,10 +4126,11 @@ def verify_bgp_community(tgen, addr_type, router, network, input_dict=None):
"""
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
- if router not in tgen.routers():
+ router_list = tgen.routers()
+ if router not in router_list:
return False
- rnode = tgen.routers()[router]
+ rnode = router_list[router]
logger.debug(
"Verifying BGP community attributes on dut %s: for %s " "network %s",
@@ -4108,11 +4257,12 @@ def verify_create_community_list(tgen, input_dict):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ router_list = tgen.routers()
for router in input_dict.keys():
- if router not in tgen.routers():
+ if router not in router_list:
continue
- rnode = tgen.routers()[router]
+ rnode = router_list[router]
logger.info("Verifying large-community is created for dut %s:", router)
@@ -4163,7 +4313,7 @@ def verify_cli_json(tgen, input_dict):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
for dut in input_dict.keys():
- rnode = tgen.routers()[dut]
+ rnode = tgen.gears[dut]
for cli in input_dict[dut]["cli"]:
logger.info(
@@ -4225,7 +4375,7 @@ def verify_evpn_vni(tgen, input_dict):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
for dut in input_dict.keys():
- rnode = tgen.routers()[dut]
+ rnode = tgen.gears[dut]
logger.info("[DUT: %s]: Verifying evpn vni details :", dut)
@@ -4343,7 +4493,7 @@ def verify_vrf_vni(tgen, input_dict):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
for dut in input_dict.keys():
- rnode = tgen.routers()[dut]
+ rnode = tgen.gears[dut]
logger.info("[DUT: %s]: Verifying vrf vni details :", dut)
@@ -4447,216 +4597,275 @@ def required_linux_kernel_version(required_version):
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))
+class HostApplicationHelper(object):
+ """Helper to track and cleanup per-host based test processes."""
- Address = [start]
- next_ip = start
+ def __init__(self, tgen=None, base_cmd=None):
+ self.base_cmd_str = ""
+ self.host_procs = {}
+ self.tgen = None
+ self.set_base_cmd(base_cmd if base_cmd else [])
+ if tgen is not None:
+ self.init(tgen)
- count = 1
- while count < repeat:
- next_ip += inc_step
- Address.append(next_ip)
- count += 1
- bindToAddress = Address
+ def __enter__(self):
+ self.init()
+ return self
- for bindTo in bindToAddress:
- iperfArgs = iperfCmd
- iperfArgs += "-B %s " % bindTo
+ def __exit__(self, type, value, traceback):
+ self.cleanup()
- # Join interval
- if join_interval:
- iperfArgs += "-i %d " % join_interval
+ def __str__(self):
+ return "HostApplicationHelper({})".format(self.base_cmd_str)
- 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)
+ def set_base_cmd(self, base_cmd):
+ assert isinstance(base_cmd, list) or isinstance(base_cmd, tuple)
+ self.base_cmd = base_cmd
+ if base_cmd:
+ self.base_cmd_str = " ".join(base_cmd)
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
+ self.base_cmd_str = ""
- # Mapped Interface IP
- if mappedAddress:
- iperfArgs += "-B %s " % mappedAddress
+ def init(self, tgen=None):
+ """Initialize the helper with tgen if needed.
- # UDP/TCP
- if l4Type == "UDP":
- iperfArgs += "-u -b 0.012m "
-
- # TTL
- if ttl:
- iperfArgs += "-T %d " % ttl
+ If overridden, need to handle multiple entries but one init. Will be called on
+ object creation if tgen is supplied. Will be called again on __enter__ so should
+ not re-init if already inited.
+ """
+ if self.tgen:
+ assert tgen is None or self.tgen == tgen
+ else:
+ self.tgen = tgen
- # Time
- if time:
- iperfArgs += "-t %d " % time
+ def started_proc(self, host, p):
+ """Called after process started on host.
- iperfArgs += " &>/dev/null &"
+ Return value is passed to `stopping_proc` method."""
+ logger.debug("%s: Doing nothing after starting process", self)
+ return False
- # 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))
+ def stopping_proc(self, host, p, info):
+ """Called after process started on host."""
+ logger.debug("%s: Doing nothing before stopping process", self)
+
+ def _add_host_proc(self, host, p):
+ v = self.started_proc(host, p)
+
+ if host not in self.host_procs:
+ self.host_procs[host] = []
+ logger.debug("%s: %s: tracking process %s", self, host, p)
+ self.host_procs[host].append((p, v))
+
+ def stop_host(self, host):
+ """Stop the process on the host.
+
+ Override to do additional cleanup."""
+ if host in self.host_procs:
+ hlogger = self.tgen.net[host].logger
+ for p, v in self.host_procs[host]:
+ self.stopping_proc(host, p, v)
+ logger.debug("%s: %s: terminating process %s", self, host, p.pid)
+ hlogger.debug("%s: %s: terminating process %s", self, host, p.pid)
+ rc = p.poll()
+ if rc is not None:
+ logger.error(
+ "%s: %s: process early exit %s: %s",
+ self,
+ host,
+ p.pid,
+ comm_error(p),
+ )
+ hlogger.error(
+ "%s: %s: process early exit %s: %s",
+ self,
+ host,
+ p.pid,
+ comm_error(p),
+ )
+ else:
+ p.terminate()
+ p.wait()
+ logger.debug(
+ "%s: %s: terminated process %s: %s",
+ self,
+ host,
+ p.pid,
+ comm_error(p),
+ )
+ hlogger.debug(
+ "%s: %s: terminated process %s: %s",
+ self,
+ host,
+ p.pid,
+ comm_error(p),
+ )
- # 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
+ del self.host_procs[host]
- logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
- return True
+ def stop_all_hosts(self):
+ hosts = set(self.host_procs)
+ for host in hosts:
+ self.stop_host(host)
+ def cleanup(self):
+ self.stop_all_hosts()
-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
+ def run(self, host, cmd_args, **kwargs):
+ cmd = list(self.base_cmd)
+ cmd.extend(cmd_args)
+ p = self.tgen.gears[host].popen(cmd, **kwargs)
+ assert p.poll() is None
+ self._add_host_proc(host, p)
+ return p
- Usage
- ----
- kill_iperf(tgen, dut ="i6", action="remove_join")
+ def check_procs(self):
+ """Check that all current processes are running, log errors if not.
- """
+ Returns: List of stopped processes."""
+ procs = []
- logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ logger.debug("%s: checking procs on hosts %s", self, self.host_procs.keys())
- 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)
+ for host in self.host_procs:
+ hlogger = self.tgen.net[host].logger
+ for p, _ in self.host_procs[host]:
+ logger.debug("%s: checking %s proc %s", self, host, p)
+ rc = p.poll()
+ if rc is None:
+ continue
+ logger.error(
+ "%s: %s proc exited: %s", self, host, comm_error(p), exc_info=True
+ )
+ hlogger.error(
+ "%s: %s proc exited: %s", self, host, comm_error(p), exc_info=True
+ )
+ procs.append(p)
+ return procs
+
+
+class IPerfHelper(HostApplicationHelper):
+ def __str__(self):
+ return "IPerfHelper()"
+
+ def run_join(
+ self,
+ host,
+ join_addr,
+ l4Type="UDP",
+ join_interval=1,
+ join_intf=None,
+ join_towards=None,
+ ):
+ """
+ Use iperf to send IGMP join and listen to traffic
+
+ Parameters:
+ -----------
+ * `host`: iperf host from where IGMP join would be sent
+ * `l4Type`: string, one of [ TCP, UDP ]
+ * `join_addr`: multicast address (or addresses) to join to
+ * `join_interval`: seconds between periodic bandwidth reports
+ * `join_intf`: the interface to bind the join to
+ * `join_towards`: router whos interface to bind the join to
+
+ returns: Success (bool)
+ """
+
+ iperf_path = self.tgen.net.get_exec_path("iperf")
+
+ assert join_addr
+ if not isinstance(join_addr, list) and not isinstance(join_addr, tuple):
+ join_addr = [ipaddress.IPv4Address(frr_unicode(join_addr))]
+
+ for bindTo in join_addr:
+ iperf_args = [iperf_path, "-s"]
+
+ if l4Type == "UDP":
+ iperf_args.append("-u")
+
+ iperf_args.append("-B")
+ if join_towards:
+ to_intf = frr_unicode(
+ self.tgen.json_topo["routers"][host]["links"][join_towards][
+ "interface"
+ ]
+ )
+ iperf_args.append("{}%{}".format(str(bindTo), to_intf))
+ elif join_intf:
+ iperf_args.append("{}%{}".format(str(bindTo), join_intf))
+ else:
+ iperf_args.append(str(bindTo))
+
+ if join_interval:
+ iperf_args.append("-i")
+ iperf_args.append(str(join_interval))
+
+ p = self.run(host, iperf_args)
+ if p.poll() is not None:
+ logger.error("IGMP join failed on %s: %s", bindTo, comm_error(p))
+ return False
+ return True
+
+ def run_traffic(
+ self, host, sentToAddress, ttl, time=0, l4Type="UDP", bind_towards=None
+ ):
+ """
+ Run iperf to send IGMP join and traffic
+
+ Parameters:
+ -----------
+ * `host`: iperf host to send traffic from
+ * `l4Type`: string, one of [ TCP, UDP ]
+ * `sentToAddress`: multicast address to send traffic to
+ * `ttl`: time to live
+ * `time`: time in seconds to transmit for
+ * `bind_towards`: Router who's interface the source ip address is got from
+
+ returns: Success (bool)
+ """
+
+ iperf_path = self.tgen.net.get_exec_path("iperf")
+
+ if sentToAddress and not isinstance(sentToAddress, list):
+ sentToAddress = [ipaddress.IPv4Address(frr_unicode(sentToAddress))]
+
+ for sendTo in sentToAddress:
+ iperf_args = [iperf_path, "-c", sendTo]
+
+ # Bind to Interface IP
+ if bind_towards:
+ ifaddr = frr_unicode(
+ self.tgen.json_topo["routers"][host]["links"][bind_towards]["ipv4"]
+ )
+ ipaddr = ipaddress.IPv4Interface(ifaddr).ip
+ iperf_args.append("-B")
+ iperf_args.append(str(ipaddr))
+
+ # UDP/TCP
+ if l4Type == "UDP":
+ iperf_args.append("-u")
+ iperf_args.append("-b")
+ iperf_args.append("0.012m")
+
+ # TTL
+ if ttl:
+ iperf_args.append("-T")
+ iperf_args.append(str(ttl))
+
+ # Time
+ if time:
+ iperf_args.append("-t")
+ iperf_args.append(str(time))
+
+ p = self.run(host, iperf_args)
+ if p.poll() is not None:
+ logger.error(
+ "mcast traffic send failed for %s: %s", sendTo, comm_error(p)
+ )
+ return False
- logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
def verify_ip_nht(tgen, input_dict):
@@ -4689,14 +4898,15 @@ def verify_ip_nht(tgen, input_dict):
logger.debug("Entering lib API: verify_ip_nht()")
+ router_list = tgen.routers()
for router in input_dict.keys():
- if router not in tgen.routers():
+ if router not in router_list:
continue
- rnode = tgen.routers()[router]
+ rnode = router_list[router]
nh_list = input_dict[router]
- if validate_ip_address(nh_list.keys()[0]) is "ipv6":
+ if validate_ip_address(next(iter(nh_list))) == "ipv6":
show_ip_nht = run_frr_cmd(rnode, "show ipv6 nht")
else:
show_ip_nht = run_frr_cmd(rnode, "show ip nht")
@@ -4713,9 +4923,7 @@ def verify_ip_nht(tgen, input_dict):
return False
-def scapy_send_raw_packet(
- tgen, topo, senderRouter, intf, packet=None, interval=1, count=1
-):
+def scapy_send_raw_packet(tgen, topo, senderRouter, intf, packet=None):
"""
Using scapy Raw() method to send BSR raw packet from one FRR
to other
@@ -4726,8 +4934,6 @@ def scapy_send_raw_packet(
* `topo` : json file data
* `senderRouter` : Sender router
* `packet` : packet in raw format
- * `interval` : Interval between the packets
- * `count` : Number of packets to be sent
returns:
--------
@@ -4749,20 +4955,11 @@ def scapy_send_raw_packet(
"data"
]
- if interval > 1 or count > 1:
- cmd = (
- "nohup /usr/bin/python {}/send_bsr_packet.py '{}' '{}' "
- "--interval={} --count={} &".format(
- CD, packet, sender_interface, interval, count
- )
- )
- else:
- cmd = (
- "/usr/bin/python {}/send_bsr_packet.py '{}' '{}' "
- "--interval={} --count={}".format(
- CD, packet, sender_interface, interval, count
- )
- )
+ python3_path = tgen.net.get_exec_path(["python3", "python"])
+ script_path = os.path.join(CD, "send_bsr_packet.py")
+ cmd = "{} {} '{}' '{}' --interval=1 --count=1".format(
+ python3_path, script_path, packet, sender_interface
+ )
logger.info("Scapy cmd: \n %s", cmd)
result = rnode.run(cmd)