Merge pull request #8964 from LabNConsulting/chopps/config-faster

tests: configure/reset routers in parallel
This commit is contained in:
Mark Stapp 2021-08-30 08:56:43 -04:00 committed by GitHub
commit 032d1a65ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 612 additions and 455 deletions

View File

@ -208,6 +208,9 @@ def test_modify_and_delete_router_id(request):
tc_name = request.node.name
write_test_header(tc_name)
# Creating configuration from JSON
reset_config_on_routers(tgen)
# Modify router id
input_dict = {
"r1": {"bgp": {"router_id": "12.12.12.12"}},
@ -252,6 +255,9 @@ def test_bgp_config_with_4byte_as_number(request):
tc_name = request.node.name
write_test_header(tc_name)
# Creating configuration from JSON
reset_config_on_routers(tgen)
input_dict = {
"r1": {"bgp": {"local_as": 131079}},
"r2": {"bgp": {"local_as": 131079}},
@ -283,6 +289,9 @@ def test_BGP_config_with_invalid_ASN_p2(request):
tc_name = request.node.name
write_test_header(tc_name)
# Creating configuration from JSON
reset_config_on_routers(tgen)
# Api call to modify AS number
input_dict = {
"r1": {
@ -307,11 +316,18 @@ def test_BGP_config_with_invalid_ASN_p2(request):
},
}
result = modify_as_number(tgen, topo, input_dict)
try:
assert result is True
except AssertionError:
logger.info("Expected behaviour: {}".format(result))
logger.info("BGP config is not created because of invalid ASNs")
assert result is not True, (
"Expected BGP config is not created because of invalid ASNs: {}".format(
result
)
)
# Creating configuration from JSON
reset_config_on_routers(tgen)
result = verify_bgp_convergence(tgen, topo)
if result != True:
assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result)
write_test_footer(tc_name)
@ -331,6 +347,13 @@ def test_BGP_config_with_2byteAS_and_4byteAS_number_p1(request):
tc_name = request.node.name
write_test_header(tc_name)
# Creating configuration from JSON
reset_config_on_routers(tgen)
result = verify_bgp_convergence(tgen, topo)
if result != True:
assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result)
# Api call to modify AS number
input_dict = {
"r1": {"bgp": {"local_as": 131079}},
@ -586,7 +609,8 @@ def test_BGP_attributes_with_vrf_default_keyword_p0(request):
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
# reset_config_on_routers(tgen)
# Creating configuration from JSON
reset_config_on_routers(tgen)
step("Configure static routes and redistribute in BGP on R3")
for addr_type in ADDR_TYPES:

View File

@ -244,11 +244,11 @@ def pytest_runtest_makereport(item, call):
modname = parent.module.__name__
# Treat skips as non errors, don't pause after
if call.excinfo.typename != "AssertionError":
if call.excinfo.typename == "Skipped":
pause = False
error = False
logger.info(
'assert skipped at "{}/{}": {}'.format(
'test skipped at "{}/{}": {}'.format(
modname, item.name, call.excinfo.value
)
)
@ -257,7 +257,7 @@ def pytest_runtest_makereport(item, call):
# Handle assert failures
parent._previousfailed = item # pylint: disable=W0212
logger.error(
'assert failed at "{}/{}": {}'.format(
'test failed at "{}/{}": {}'.format(
modname, item.name, call.excinfo.value
)
)

View File

@ -33,7 +33,7 @@ from lib.topotest import frr_unicode
# Import common_config to use commomnly used APIs
from lib.common_config import (
create_common_configuration,
create_common_configurations,
InvalidCLIError,
load_config_to_router,
check_address_types,
@ -148,6 +148,8 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
topo = topo["routers"]
input_dict = deepcopy(input_dict)
config_data_dict = {}
for router in input_dict.keys():
if "bgp" not in input_dict[router]:
logger.debug("Router %s: 'bgp' not present in input_dict", router)
@ -158,6 +160,8 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
if type(bgp_data_list) is not list:
bgp_data_list = [bgp_data_list]
config_data = []
for bgp_data in bgp_data_list:
data_all_bgp = __create_bgp_global(tgen, bgp_data, router, build)
if data_all_bgp:
@ -198,16 +202,19 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
data_all_bgp = __create_l2vpn_evpn_address_family(
tgen, topo, bgp_data, router, config_data=data_all_bgp
)
if data_all_bgp:
config_data.extend(data_all_bgp)
try:
result = create_common_configuration(
tgen, router, data_all_bgp, "bgp", build, load_config
)
except InvalidCLIError:
# Traceback
errormsg = traceback.format_exc()
logger.error(errormsg)
return errormsg
if config_data:
config_data_dict[router] = config_data
try:
result = create_common_configurations(
tgen, config_data_dict, "bgp", build, load_config
)
except InvalidCLIError:
logger.error("create_router_bgp", exc_info=True)
result = False
logger.debug("Exiting lib API: create_router_bgp()")
return result
@ -226,7 +233,7 @@ def __create_bgp_global(tgen, input_dict, router, build=False):
Returns
-------
True or False
list of config commands
"""
result = False
@ -241,7 +248,7 @@ def __create_bgp_global(tgen, input_dict, router, build=False):
logger.debug(
"Router %s: 'local_as' not present in input_dict" "for BGP", router
)
return False
return config_data
local_as = bgp_data.setdefault("local_as", "")
cmd = "router bgp {}".format(local_as)
@ -1532,15 +1539,16 @@ def modify_as_number(tgen, topo, input_dict):
create_router_bgp(tgen, topo, router_dict)
logger.info("Applying modified bgp configuration")
create_router_bgp(tgen, new_topo)
result = create_router_bgp(tgen, new_topo)
if result is not True:
result = "Error applying new AS number config"
except Exception as e:
errormsg = traceback.format_exc()
logger.error(errormsg)
return errormsg
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return True
return result
@retry(retry_timeout=8)

View File

@ -27,6 +27,7 @@ from re import search as re_search
from tempfile import mkdtemp
import json
import logging
import os
import sys
import traceback
@ -275,7 +276,8 @@ def apply_raw_config(tgen, input_dict):
True or errormsg
"""
result = True
rlist = []
for router_name in input_dict.keys():
config_cmd = input_dict[router_name]["raw_config"]
@ -287,13 +289,14 @@ def apply_raw_config(tgen, input_dict):
for cmd in config_cmd:
cfg.write("{}\n".format(cmd))
result = load_config_to_router(tgen, router_name)
rlist.append(router_name)
return result
# Load config on all routers
return load_config_to_routers(tgen, rlist)
def create_common_configuration(
tgen, router, data, config_type=None, build=False, load_config=True
def create_common_configurations(
tgen, config_dict, config_type=None, build=False, load_config=True
):
"""
API to create object of class FRRConfig and also create frr_json.conf
@ -302,8 +305,8 @@ def create_common_configuration(
Parameters
----------
* `tgen`: tgen object
* `data`: Configuration data saved in a list.
* `router` : router id to be configured.
* `config_dict`: Configuration data saved in a dict of { router: config-list }
* `routers` : list of 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.
* `build` : Only for initial setup phase this is set as True
@ -313,8 +316,6 @@ def create_common_configuration(
"""
TMPDIR = os.path.join(LOGDIR, tgen.modname)
fname = "{}/{}/{}".format(TMPDIR, router, FRRCFG_FILE)
config_map = OrderedDict(
{
"general_config": "! FRR General Config\n",
@ -339,27 +340,55 @@ def create_common_configuration(
else:
mode = "w"
try:
frr_cfg_fd = open(fname, mode)
if config_type:
frr_cfg_fd.write(config_map[config_type])
for line in data:
frr_cfg_fd.write("{} \n".format(str(line)))
frr_cfg_fd.write("\n")
routers = config_dict.keys()
for router in routers:
fname = "{}/{}/{}".format(TMPDIR, router, FRRCFG_FILE)
try:
frr_cfg_fd = open(fname, mode)
if config_type:
frr_cfg_fd.write(config_map[config_type])
for line in config_dict[router]:
frr_cfg_fd.write("{} \n".format(str(line)))
frr_cfg_fd.write("\n")
except IOError as err:
logger.error(
"Unable to open FRR Config File. error(%s): %s" % (err.errno, err.strerror)
)
return False
finally:
frr_cfg_fd.close()
except IOError as err:
logger.error(
"Unable to open FRR Config '%s': %s" % (fname, str(err))
)
return False
finally:
frr_cfg_fd.close()
# If configuration applied from build, it will done at last
result = True
if not build and load_config:
load_config_to_router(tgen, router)
result = load_config_to_routers(tgen, routers)
return True
return result
def create_common_configuration(
tgen, router, data, config_type=None, build=False, load_config=True
):
"""
API to create object of class FRRConfig and also create frr_json.conf
file. It will create interface and common configurations and save it to
frr_json.conf and load to router
Parameters
----------
* `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.
* `build` : Only for initial setup phase this is set as True
Returns
-------
True or False
"""
return create_common_configurations(
tgen, {router: data}, config_type, build, load_config
)
def kill_router_daemons(tgen, router, daemons, save_config=True):
@ -541,8 +570,8 @@ def reset_config_on_routers(tgen, routerName=None):
'\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output)
)
else:
router_list[rname].logger.error(
'\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output)
router_list[rname].logger.warning(
'\nvtysh config apply failed => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output)
)
logger.error("Delta file apply for %s failed %d: %s", rname, p.returncode, output)
@ -570,16 +599,114 @@ 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 {} after reset:\n{}".format(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 load_config_to_routers(tgen, routers, save_bkup=False):
"""
Loads configuration on routers from the file FRRCFG_FILE.
Parameters
----------
* `tgen` : Topogen object
* `routers` : routers for which configuration is to be loaded
* `save_bkup` : If True, Saves snapshot of FRRCFG_FILE to FRRCFG_BKUP_FILE
Returns
-------
True or False
"""
logger.debug("Entering API: load_config_to_routers")
base_router_list = tgen.routers()
router_list = {}
for router in routers:
if (router not in ROUTER_LIST) or (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
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)
with open(frr_cfg_file, "r+") as cfg:
data = cfg.read()
logger.info(
"Applying following configuration on router"
" {}:\n{}".format(rname, data)
)
if save_bkup:
with open(frr_cfg_bkup, "w") as bkup:
bkup.write(data)
procs[rname] = router_list[rname].popen(
["/usr/bin/env", "vtysh", "-f", frr_cfg_file],
stdin=None,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
except IOError as err:
logging.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))
return False
errors = []
for rname, p in procs.items():
output, _ = p.communicate()
frr_cfg_file = frr_cfg_file_fmt.format(rname)
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)
)
else:
router_list[rname].logger.error(
'\nvtysh config apply failed => "{}"\nvtysh output <= "{}"'.format(vtysh_command, 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)))
# Empty the config file or we append to it next time through.
with open(frr_cfg_file, "r+") as cfg:
cfg.truncate(0)
# Router current configuration to log file or console if
# "show_router_config" is defined in "pytest.ini"
if show_router_config:
procs = {}
for rname in router_list:
procs[rname] = router_list[rname].popen(
["/usr/bin/env", "vtysh", "-c", "show running-config no-header"],
stdin=None,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
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)
else:
logger.info("New configuration for router %s:\n%s", rname,output)
logger.debug("Exiting API: load_config_to_routers")
return not errors
def load_config_to_router(tgen, routerName, save_bkup=False):
"""
Loads configuration on router from the file FRRCFG_FILE.
@ -590,52 +717,7 @@ def load_config_to_router(tgen, routerName, save_bkup=False):
* `routerName` : router for which configuration to be loaded
* `save_bkup` : If True, Saves snapshot of FRRCFG_FILE to FRRCFG_BKUP_FILE
"""
logger.debug("Entering API: load_config_to_router")
router_list = tgen.routers()
for rname in ROUTER_LIST:
if routerName and rname != routerName:
continue
router = router_list[rname]
try:
frr_cfg_file = "{}/{}/{}".format(TMPDIR, rname, FRRCFG_FILE)
frr_cfg_bkup = "{}/{}/{}".format(TMPDIR, rname, FRRCFG_BKUP_FILE)
with open(frr_cfg_file, "r+") as cfg:
data = cfg.read()
logger.info(
"Applying following configuration on router"
" {}:\n{}".format(rname, data)
)
if save_bkup:
with open(frr_cfg_bkup, "w") as bkup:
bkup.write(data)
output = router.vtysh_multicmd(data, pretty_output=False)
for out_err in ERROR_LIST:
if out_err.lower() in output.lower():
raise InvalidCLIError("%s" % output)
cfg.truncate(0)
except IOError as err:
errormsg = (
"Unable to open config File. error(%s):" " %s",
(err.errno, err.strerror),
)
return errormsg
# Router current configuration to log file or console if
# "show_router_config" is defined in "pytest.ini"
if show_router_config:
logger.info("New configuration for router {}:".format(rname))
new_config = router.run("vtysh -c 'show running'")
logger.info(new_config)
logger.debug("Exiting API: load_config_to_router")
return True
return load_config_to_routers(tgen, [routerName], save_bkup)
def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None):
@ -1174,6 +1256,8 @@ def create_debug_log_config(tgen, input_dict, build=False):
result = False
try:
debug_config_dict = {}
for router in input_dict.keys():
debug_config = []
if "debug" in input_dict[router]:
@ -1204,10 +1288,12 @@ def create_debug_log_config(tgen, input_dict, build=False):
for daemon, debug_logs in disable_logs.items():
for debug_log in debug_logs:
debug_config.append("no {}".format(debug_log))
if debug_config:
debug_config_dict[router] = debug_config
result = create_common_configuration(
tgen, router, debug_config, "debug_log_config", build=build
)
result = create_common_configurations(
tgen, debug_config_dict, "debug_log_config", build=build
)
except InvalidCLIError:
# Traceback
errormsg = traceback.format_exc()
@ -1285,11 +1371,14 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False):
input_dict = deepcopy(input_dict)
try:
config_data_dict = {}
for c_router, c_data in input_dict.items():
rnode = tgen.routers()[c_router]
config_data = []
if "vrfs" in c_data:
for vrf in c_data["vrfs"]:
config_data = []
del_action = vrf.setdefault("delete", False)
name = vrf.setdefault("name", None)
table_id = vrf.setdefault("id", None)
@ -1366,9 +1455,12 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False):
cmd = "no vni {}".format(del_vni)
config_data.append(cmd)
result = create_common_configuration(
tgen, c_router, config_data, "vrf", build=build
)
if config_data:
config_data_dict[c_router] = config_data
result = create_common_configurations(
tgen, config_data_dict, "vrf", build=build
)
except InvalidCLIError:
# Traceback
@ -1638,7 +1730,8 @@ def interface_status(tgen, topo, input_dict):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
try:
global frr_cfg
rlist = []
for router in input_dict.keys():
interface_list = input_dict[router]["interface_list"]
@ -1647,8 +1740,10 @@ def interface_status(tgen, topo, input_dict):
rnode = tgen.routers()[router]
interface_set_status(rnode, intf, status)
# Load config to router
load_config_to_router(tgen, router)
rlist.append(router)
# Load config to routers
load_config_to_routers(tgen, rlist)
except Exception as e:
errormsg = traceback.format_exc()
@ -1837,6 +1932,8 @@ def create_interfaces_cfg(tgen, topo, build=False):
topo = deepcopy(topo)
try:
interface_data_dict = {}
for c_router, c_data in topo.items():
interface_data = []
for destRouterLink, data in sorted(c_data["links"].items()):
@ -1903,10 +2000,12 @@ def create_interfaces_cfg(tgen, topo, build=False):
interface_data += _create_interfaces_ospf_cfg(
"ospf6", c_data, data, ospf_keywords + ["area"]
)
if interface_data:
interface_data_dict[c_router] = interface_data
result = create_common_configuration(
tgen, c_router, interface_data, "interface_config", build=build
)
result = create_common_configurations(
tgen, interface_data_dict, "interface_config", build=build
)
except InvalidCLIError:
# Traceback
@ -1965,6 +2064,8 @@ def create_static_routes(tgen, input_dict, build=False):
input_dict = deepcopy(input_dict)
try:
static_routes_list_dict = {}
for router in input_dict.keys():
if "static_routes" not in input_dict[router]:
errormsg = "static_routes not present in input_dict"
@ -2020,9 +2121,12 @@ def create_static_routes(tgen, input_dict, build=False):
static_routes_list.append(cmd)
result = create_common_configuration(
tgen, router, static_routes_list, "static_route", build=build
)
if static_routes_list:
static_routes_list_dict[router] = static_routes_list
result = create_common_configurations(
tgen, static_routes_list_dict, "static_route", build=build
)
except InvalidCLIError:
# Traceback
@ -2079,6 +2183,8 @@ def create_prefix_lists(tgen, input_dict, build=False):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
result = False
try:
config_data_dict = {}
for router in input_dict.keys():
if "prefix_lists" not in input_dict[router]:
errormsg = "prefix_lists not present in input_dict"
@ -2125,9 +2231,12 @@ def create_prefix_lists(tgen, input_dict, build=False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
result = create_common_configuration(
tgen, router, config_data, "prefix_list", build=build
)
if config_data:
config_data_dict[router] = config_data
result = create_common_configurations(
tgen, config_data_dict, "prefix_list", build=build
)
except InvalidCLIError:
# Traceback
@ -2223,6 +2332,8 @@ def create_route_maps(tgen, input_dict, build=False):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
input_dict = deepcopy(input_dict)
try:
rmap_data_dict = {}
for router in input_dict.keys():
if "route_maps" not in input_dict[router]:
logger.debug("route_maps not present in input_dict")
@ -2500,9 +2611,12 @@ def create_route_maps(tgen, input_dict, build=False):
cmd = "match metric {}".format(metric)
rmap_data.append(cmd)
result = create_common_configuration(
tgen, router, rmap_data, "route_maps", build=build
)
if rmap_data:
rmap_data_dict[router] = rmap_data
result = create_common_configurations(
tgen, rmap_data_dict, "route_maps", build=build
)
except InvalidCLIError:
# Traceback
@ -2577,6 +2691,8 @@ def create_bgp_community_lists(tgen, input_dict, build=False):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
input_dict = deepcopy(input_dict)
try:
config_data_dict = {}
for router in input_dict.keys():
if "bgp_community_lists" not in input_dict[router]:
errormsg = "bgp_community_lists not present in input_dict"
@ -2613,9 +2729,12 @@ def create_bgp_community_lists(tgen, input_dict, build=False):
config_data.append(cmd)
result = create_common_configuration(
tgen, router, config_data, "bgp_community_list", build=build
)
if config_data:
config_data_dict[router] = config_data
result = create_common_configurations(
tgen, config_data_dict, "bgp_community_list", build=build
)
except InvalidCLIError:
# Traceback

View File

@ -18,7 +18,6 @@
# OF THIS SOFTWARE.
#
import traceback
import ipaddr
import ipaddress
import sys
@ -32,7 +31,7 @@ import sys
# Import common_config to use commomnly used APIs
from lib.common_config import (
create_common_configuration,
create_common_configurations,
InvalidCLIError,
retry,
generate_ips,
@ -86,32 +85,36 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru
topo = topo["routers"]
input_dict = deepcopy(input_dict)
for router in input_dict.keys():
if "ospf" not in input_dict[router]:
logger.debug("Router %s: 'ospf' not present in input_dict", router)
continue
for ospf in ["ospf", "ospf6"]:
config_data_dict = {}
result = __create_ospf_global(tgen, input_dict, router, build, load_config)
if result is True:
ospf_data = input_dict[router]["ospf"]
for router in input_dict.keys():
if ospf not in input_dict[router]:
logger.debug("Router %s: %s not present in input_dict", router, ospf)
continue
for router in input_dict.keys():
if "ospf6" not in input_dict[router]:
logger.debug("Router %s: 'ospf6' not present in input_dict", router)
continue
result = __create_ospf_global(
tgen, input_dict, router, build, load_config, ospf="ospf6"
)
if result is True:
ospf_data = input_dict[router]["ospf6"]
config_data = __create_ospf_global(
tgen, input_dict, router, build, load_config, ospf
)
if config_data:
if router not in config_data_dict:
config_data_dict[router] = config_data
else:
config_data_dict[router].extend(config_data)
try:
result = create_common_configurations(
tgen, config_data_dict, ospf, build, load_config
)
except InvalidCLIError:
logger.error("create_router_ospf (ipv4)", exc_info=True)
result = False
logger.debug("Exiting lib API: create_router_ospf()")
return result
def __create_ospf_global(
tgen, input_dict, router, build=False, load_config=True, ospf="ospf"
tgen, input_dict, router, build, load_config, ospf
):
"""
Helper API to create ospf global configuration.
@ -133,12 +136,12 @@ def __create_ospf_global(
"links": {
"r3": {
"ipv6": "2013:13::1/64",
"ospf6": {
"ospf6": {
"hello_interval": 1,
"dead_interval": 4,
"network": "point-to-point"
}
}
}
},
"ospf6": {
"router_id": "1.1.1.1",
@ -153,229 +156,221 @@ def __create_ospf_global(
Returns
-------
True or False
list of configuration commands
"""
result = False
config_data = []
if ospf not in input_dict[router]:
return config_data
logger.debug("Entering lib API: __create_ospf_global()")
try:
ospf_data = input_dict[router][ospf]
del_ospf_action = ospf_data.setdefault("delete", False)
if del_ospf_action:
config_data = ["no router {}".format(ospf)]
result = create_common_configuration(
tgen, router, config_data, ospf, build, load_config
)
return result
ospf_data = input_dict[router][ospf]
del_ospf_action = ospf_data.setdefault("delete", False)
if del_ospf_action:
config_data = ["no router {}".format(ospf)]
return config_data
config_data = []
cmd = "router {}".format(ospf)
cmd = "router {}".format(ospf)
config_data.append(cmd)
# router id
router_id = ospf_data.setdefault("router_id", None)
del_router_id = ospf_data.setdefault("del_router_id", False)
if del_router_id:
config_data.append("no {} router-id".format(ospf))
if router_id:
config_data.append("{} router-id {}".format(ospf, router_id))
# log-adjacency-changes
log_adj_changes = ospf_data.setdefault("log_adj_changes", None)
del_log_adj_changes = ospf_data.setdefault("del_log_adj_changes", False)
if del_log_adj_changes:
config_data.append("no log-adjacency-changes detail")
if log_adj_changes:
config_data.append("log-adjacency-changes {}".format(
log_adj_changes))
# aggregation timer
aggr_timer = ospf_data.setdefault("aggr_timer", None)
del_aggr_timer = ospf_data.setdefault("del_aggr_timer", False)
if del_aggr_timer:
config_data.append("no aggregation timer")
if aggr_timer:
config_data.append("aggregation timer {}".format(
aggr_timer))
# maximum path information
ecmp_data = ospf_data.setdefault("maximum-paths", {})
if ecmp_data:
cmd = "maximum-paths {}".format(ecmp_data)
del_action = ospf_data.setdefault("del_max_path", False)
if del_action:
cmd = "no maximum-paths"
config_data.append(cmd)
# router id
router_id = ospf_data.setdefault("router_id", None)
del_router_id = ospf_data.setdefault("del_router_id", False)
if del_router_id:
config_data.append("no {} router-id".format(ospf))
if router_id:
config_data.append("{} router-id {}".format(ospf, router_id))
# log-adjacency-changes
log_adj_changes = ospf_data.setdefault("log_adj_changes", None)
del_log_adj_changes = ospf_data.setdefault("del_log_adj_changes", False)
if del_log_adj_changes:
config_data.append("no log-adjacency-changes detail")
if log_adj_changes:
config_data.append("log-adjacency-changes {}".format(log_adj_changes))
# aggregation timer
aggr_timer = ospf_data.setdefault("aggr_timer", None)
del_aggr_timer = ospf_data.setdefault("del_aggr_timer", False)
if del_aggr_timer:
config_data.append("no aggregation timer")
if aggr_timer:
config_data.append("aggregation timer {}".format(aggr_timer))
# maximum path information
ecmp_data = ospf_data.setdefault("maximum-paths", {})
if ecmp_data:
cmd = "maximum-paths {}".format(ecmp_data)
del_action = ospf_data.setdefault("del_max_path", False)
if del_action:
cmd = "no maximum-paths"
config_data.append(cmd)
# redistribute command
redistribute_data = ospf_data.setdefault("redistribute", {})
if redistribute_data:
for redistribute in redistribute_data:
if "redist_type" not in redistribute:
logger.debug(
"Router %s: 'redist_type' not present in " "input_dict", router
)
else:
cmd = "redistribute {}".format(redistribute["redist_type"])
for red_type in redistribute_data:
if "route_map" in red_type:
cmd = cmd + " route-map {}".format(red_type["route_map"])
del_action = redistribute.setdefault("delete", False)
if del_action:
cmd = "no {}".format(cmd)
config_data.append(cmd)
# area information
area_data = ospf_data.setdefault("area", {})
if area_data:
for area in area_data:
if "id" not in area:
logger.debug(
"Router %s: 'area id' not present in " "input_dict", router
)
else:
cmd = "area {}".format(area["id"])
if "type" in area:
cmd = cmd + " {}".format(area["type"])
del_action = area.setdefault("delete", False)
if del_action:
cmd = "no {}".format(cmd)
config_data.append(cmd)
# def route information
def_rte_data = ospf_data.setdefault("default-information", {})
if def_rte_data:
if "originate" not in def_rte_data:
# redistribute command
redistribute_data = ospf_data.setdefault("redistribute", {})
if redistribute_data:
for redistribute in redistribute_data:
if "redist_type" not in redistribute:
logger.debug(
"Router %s: 'originate key' not present in " "input_dict", router
"Router %s: 'redist_type' not present in " "input_dict", router
)
else:
cmd = "default-information originate"
if "always" in def_rte_data:
cmd = cmd + " always"
if "metric" in def_rte_data:
cmd = cmd + " metric {}".format(def_rte_data["metric"])
if "metric-type" in def_rte_data:
cmd = cmd + " metric-type {}".format(def_rte_data["metric-type"])
if "route-map" in def_rte_data:
cmd = cmd + " route-map {}".format(def_rte_data["route-map"])
del_action = def_rte_data.setdefault("delete", False)
cmd = "redistribute {}".format(redistribute["redist_type"])
for red_type in redistribute_data:
if "route_map" in red_type:
cmd = cmd + " route-map {}".format(red_type["route_map"])
del_action = redistribute.setdefault("delete", False)
if del_action:
cmd = "no {}".format(cmd)
config_data.append(cmd)
# area interface information for ospf6d only
if ospf == "ospf6":
area_iface = ospf_data.setdefault("neighbors", {})
if area_iface:
for neighbor in area_iface:
if "area" in area_iface[neighbor]:
# area information
area_data = ospf_data.setdefault("area", {})
if area_data:
for area in area_data:
if "id" not in area:
logger.debug(
"Router %s: 'area id' not present in " "input_dict", router
)
else:
cmd = "area {}".format(area["id"])
if "type" in area:
cmd = cmd + " {}".format(area["type"])
del_action = area.setdefault("delete", False)
if del_action:
cmd = "no {}".format(cmd)
config_data.append(cmd)
#def route information
def_rte_data = ospf_data.setdefault("default-information", {})
if def_rte_data:
if "originate" not in def_rte_data:
logger.debug("Router %s: 'originate key' not present in "
"input_dict", router)
else:
cmd = "default-information originate"
if "always" in def_rte_data:
cmd = cmd + " always"
if "metric" in def_rte_data:
cmd = cmd + " metric {}".format(def_rte_data["metric"])
if "metric-type" in def_rte_data:
cmd = cmd + " metric-type {}".format(def_rte_data[
"metric-type"])
if "route-map" in def_rte_data:
cmd = cmd + " route-map {}".format(def_rte_data["route-map"])
del_action = def_rte_data.setdefault("delete", False)
if del_action:
cmd = "no {}".format(cmd)
config_data.append(cmd)
# area interface information for ospf6d only
if ospf == "ospf6":
area_iface = ospf_data.setdefault("neighbors", {})
if area_iface:
for neighbor in area_iface:
if "area" in area_iface[neighbor]:
iface = input_dict[router]["links"][neighbor]["interface"]
cmd = "interface {} area {}".format(
iface, area_iface[neighbor]["area"]
)
if area_iface[neighbor].setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
try:
if "area" in input_dict[router]['links'][neighbor][
'ospf6']:
iface = input_dict[router]["links"][neighbor]["interface"]
cmd = "interface {} area {}".format(
iface, area_iface[neighbor]["area"]
)
if area_iface[neighbor].setdefault("delete", False):
iface, input_dict[router]['links'][neighbor][
'ospf6']['area'])
if input_dict[router]['links'][neighbor].setdefault(
"delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
try:
if "area" in input_dict[router]["links"][neighbor]["ospf6"]:
iface = input_dict[router]["links"][neighbor]["interface"]
cmd = "interface {} area {}".format(
iface,
input_dict[router]["links"][neighbor]["ospf6"]["area"],
)
if input_dict[router]["links"][neighbor].setdefault(
"delete", False
):
cmd = "no {}".format(cmd)
config_data.append(cmd)
except KeyError:
except KeyError:
pass
# summary information
summary_data = ospf_data.setdefault("summary-address", {})
if summary_data:
for summary in summary_data:
if "prefix" not in summary:
logger.debug(
"Router %s: 'summary-address' not present in " "input_dict",
router,
)
else:
cmd = "summary {}/{}".format(summary["prefix"], summary["mask"])
_tag = summary.setdefault("tag", None)
if _tag:
cmd = "{} tag {}".format(cmd, _tag)
_advertise = summary.setdefault("advertise", True)
if not _advertise:
cmd = "{} no-advertise".format(cmd)
del_action = summary.setdefault("delete", False)
if del_action:
cmd = "no {}".format(cmd)
config_data.append(cmd)
# ospf gr information
gr_data = ospf_data.setdefault("graceful-restart", {})
if gr_data:
if "opaque" in gr_data and gr_data["opaque"]:
cmd = "capability opaque"
if gr_data.setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
if "helper-only" in gr_data and not gr_data["helper-only"]:
cmd = "graceful-restart helper-only"
if gr_data.setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
elif "helper-only" in gr_data and type(gr_data["helper-only"]) is list:
for rtrs in gr_data["helper-only"]:
cmd = "graceful-restart helper-only {}".format(rtrs)
if gr_data.setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
if "helper" in gr_data:
if type(gr_data["helper"]) is not list:
gr_data["helper"] = list(gr_data["helper"])
for helper_role in gr_data["helper"]:
cmd = "graceful-restart helper {}".format(helper_role)
if gr_data.setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
if "supported-grace-time" in gr_data:
cmd = "graceful-restart helper supported-grace-time {}".format(
gr_data["supported-grace-time"]
# summary information
summary_data = ospf_data.setdefault("summary-address", {})
if summary_data:
for summary in summary_data:
if "prefix" not in summary:
logger.debug(
"Router %s: 'summary-address' not present in " "input_dict",
router,
)
else:
cmd = "summary {}/{}".format(summary["prefix"], summary["mask"])
_tag = summary.setdefault("tag", None)
if _tag:
cmd = "{} tag {}".format(cmd, _tag)
_advertise = summary.setdefault("advertise", True)
if not _advertise:
cmd = "{} no-advertise".format(cmd)
del_action = summary.setdefault("delete", False)
if del_action:
cmd = "no {}".format(cmd)
config_data.append(cmd)
# ospf gr information
gr_data = ospf_data.setdefault("graceful-restart", {})
if gr_data:
if "opaque" in gr_data and gr_data["opaque"]:
cmd = "capability opaque"
if gr_data.setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
if "helper-only" in gr_data and not gr_data["helper-only"]:
cmd = "graceful-restart helper-only"
if gr_data.setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
elif "helper-only" in gr_data and type(gr_data["helper-only"]) is list:
for rtrs in gr_data["helper-only"]:
cmd = "graceful-restart helper-only {}".format(rtrs)
if gr_data.setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
result = create_common_configuration(
tgen, router, config_data, "ospf", build, load_config
)
if "helper" in gr_data:
if type(gr_data["helper"]) is not list:
gr_data["helper"] = list(gr_data["helper"])
for helper_role in gr_data["helper"]:
cmd = "graceful-restart helper {}".format(helper_role)
if gr_data.setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
except InvalidCLIError:
# Traceback
errormsg = traceback.format_exc()
logger.error(errormsg)
return errormsg
if "supported-grace-time" in gr_data:
cmd = "graceful-restart helper supported-grace-time {}".format(
gr_data["supported-grace-time"]
)
if gr_data.setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
logger.debug("Exiting lib API: create_ospf_global()")
return result
return config_data
def create_router_ospf6(tgen, topo, input_dict=None, build=False, load_config=True):
@ -410,14 +405,27 @@ def create_router_ospf6(tgen, topo, input_dict=None, build=False, load_config=Tr
else:
topo = topo["routers"]
input_dict = deepcopy(input_dict)
config_data_dict = {}
for router in input_dict.keys():
if "ospf6" not in input_dict[router]:
logger.debug("Router %s: 'ospf6' not present in input_dict", router)
continue
result = __create_ospf_global(
config_data = __create_ospf_global(
tgen, input_dict, router, build, load_config, "ospf6"
)
if config_data:
config_data_dict[router] = config_data
try:
result = create_common_configurations(
tgen, config_data_dict, "ospf6", build, load_config
)
except InvalidCLIError:
logger.error("create_router_ospf6", exc_info=True)
result = False
logger.debug("Exiting lib API: create_router_ospf6()")
return result
@ -462,6 +470,9 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config=
input_dict = deepcopy(topo)
else:
input_dict = deepcopy(input_dict)
config_data_dict = {}
for router in input_dict.keys():
config_data = []
for lnk in input_dict[router]["links"].keys():
@ -546,10 +557,14 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config=
if build:
return config_data
else:
result = create_common_configuration(
tgen, router, config_data, "interface_config", build=build
)
if config_data:
config_data_dict[router] = config_data
result = create_common_configurations(
tgen, config_data_dict, "interface_config", build=build
)
logger.debug("Exiting lib API: config_ospf_interface()")
return result
@ -2339,6 +2354,9 @@ def config_ospf6_interface(tgen, topo, input_dict=None, build=False, load_config
input_dict = deepcopy(topo)
else:
input_dict = deepcopy(input_dict)
config_data_dict = {}
for router in input_dict.keys():
config_data = []
for lnk in input_dict[router]['links'].keys():
@ -2409,10 +2427,14 @@ def config_ospf6_interface(tgen, topo, input_dict=None, build=False, load_config
if build:
return config_data
else:
result = create_common_configuration(
tgen, router, config_data, "interface_config", build=build
)
if config_data:
config_data_dict[router] = config_data
result = create_common_configurations(
tgen, config_data_dict, "interface_config", build=build
)
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result

View File

@ -29,6 +29,7 @@ from lib.topolog import logger
# Import common_config to use commomnly used APIs
from lib.common_config import (
create_common_configuration,
create_common_configurations,
InvalidCLIError,
retry,
run_frr_cmd,
@ -79,28 +80,38 @@ def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True
else:
topo = topo["routers"]
input_dict = deepcopy(input_dict)
config_data_dict = {}
for router in input_dict.keys():
result = _enable_disable_pim(tgen, topo, input_dict, router, build)
config_data = _enable_disable_pim_config(tgen, topo, input_dict, router, build)
if config_data:
config_data_dict[router] = config_data
# Now add RP config to all routers
for router in input_dict.keys():
if "pim" not in input_dict[router]:
logger.debug("Router %s: 'pim' is not present in " "input_dict", router)
continue
if "rp" not in input_dict[router]["pim"]:
continue
_add_pim_rp_config(
tgen, topo, input_dict, router, build, config_data_dict
)
if result is True:
if "rp" not in input_dict[router]["pim"]:
continue
result = _create_pim_rp_config(
tgen, topo, input_dict, router, build, load_config
)
if result is not True:
return False
try:
result = create_common_configurations(
tgen, config_data_dict, "pim", build, load_config
)
except InvalidCLIError:
logger.error("create_pim_config", exc_info=True)
result = False
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result
def _create_pim_rp_config(tgen, topo, input_dict, router, build=False, load_config=False):
def _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict):
"""
Helper API to create pim RP configurations.
@ -111,13 +122,12 @@ def _create_pim_rp_config(tgen, topo, input_dict, router, build=False, load_conf
* `input_dict` : Input dict data, required when configuring from testcase
* `router` : router id to be configured.
* `build` : Only for initial setup phase this is set as True.
* `config_data_dict` : OUT: adds `router` config to dictinary
Returns
-------
True or False
None
"""
result = False
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
pim_data = input_dict[router]["pim"]
@ -125,7 +135,6 @@ def _create_pim_rp_config(tgen, topo, input_dict, router, build=False, load_conf
# Configure this RP on every router.
for dut in tgen.routers():
# At least one interface must be enabled for PIM on the router
pim_if_enabled = False
for destLink, data in topo[dut]["links"].items():
@ -193,22 +202,11 @@ def _create_pim_rp_config(tgen, topo, input_dict, router, build=False, load_conf
cmd = "no {}".format(cmd)
config_data.append(cmd)
try:
result = create_common_configuration(
tgen, dut, config_data, "pim", build, load_config
)
if result is not True:
logger.error("Error applying PIM config", exc_info=True)
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return False
except InvalidCLIError as error:
logger.error("Error applying PIM config: %s", error, exc_info=error)
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return False
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result
if config_data:
if dut not in config_data_dict:
config_data_dict[dut] = config_data
else:
config_data_dict[dut].extend(config_data)
def create_igmp_config(tgen, topo, input_dict=None, build=False):
@ -255,6 +253,9 @@ def create_igmp_config(tgen, topo, input_dict=None, build=False):
else:
topo = topo["routers"]
input_dict = deepcopy(input_dict)
config_data_dict = {}
for router in input_dict.keys():
if "igmp" not in input_dict[router]:
logger.debug("Router %s: 'igmp' is not present in " "input_dict", router)
@ -300,21 +301,22 @@ def create_igmp_config(tgen, topo, input_dict=None, build=False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
try:
if config_data:
config_data_dict[router] = config_data
result = create_common_configuration(
tgen, router, config_data, "interface_config", build=build
)
except InvalidCLIError:
errormsg = traceback.format_exc()
logger.error(errormsg)
return errormsg
try:
result = create_common_configurations(
tgen, config_data_dict, "interface_config", build=build
)
except InvalidCLIError:
logger.error("create_igmp_config", exc_info=True)
result = False
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result
def _enable_disable_pim(tgen, topo, input_dict, router, build=False):
def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False):
"""
Helper API to enable or disable pim on interfaces
@ -328,57 +330,40 @@ def _enable_disable_pim(tgen, topo, input_dict, router, build=False):
Returns
-------
True or False
list of config
"""
result = False
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
try:
config_data = []
# Enable pim on interfaces
for destRouterLink, data in sorted(topo[router]["links"].items()):
if "pim" in data and data["pim"] == "enable":
config_data = []
# Loopback interfaces
if "type" in data and data["type"] == "loopback":
interface_name = destRouterLink
else:
interface_name = data["interface"]
# Enable pim on interfaces
for destRouterLink, data in sorted(topo[router]["links"].items()):
if "pim" in data and data["pim"] == "enable":
# Loopback interfaces
if "type" in data and data["type"] == "loopback":
interface_name = destRouterLink
else:
interface_name = data["interface"]
cmd = "interface {}".format(interface_name)
cmd = "interface {}".format(interface_name)
config_data.append(cmd)
config_data.append("ip pim")
# pim global config
if "pim" in input_dict[router]:
pim_data = input_dict[router]["pim"]
del_action = pim_data.setdefault("delete", False)
for t in [
"join-prune-interval",
"keep-alive-timer",
"register-suppress-time",
]:
if t in pim_data:
cmd = "ip pim {} {}".format(t, pim_data[t])
if del_action:
cmd = "no {}".format(cmd)
config_data.append(cmd)
config_data.append("ip pim")
result = create_common_configuration(
tgen, router, config_data, "interface_config", build=build
)
if result is not True:
return False
config_data = []
if "pim" in input_dict[router]:
pim_data = input_dict[router]["pim"]
for t in [
"join-prune-interval",
"keep-alive-timer",
"register-suppress-time",
]:
if t in pim_data:
cmd = "ip pim {} {}".format(t, pim_data[t])
config_data.append(cmd)
if config_data:
result = create_common_configuration(
tgen, router, config_data, "pim", build=build
)
except InvalidCLIError:
# Traceback
errormsg = traceback.format_exc()
logger.error(errormsg)
return errormsg
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result
return config_data
def find_rp_details(tgen, topo):
@ -451,7 +436,9 @@ def configure_pim_force_expire(tgen, topo, input_dict, build=False):
result = False
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
try:
config_data_dict = {}
for dut in input_dict.keys():
if "pim" not in input_dict[dut]:
@ -459,8 +446,8 @@ def configure_pim_force_expire(tgen, topo, input_dict, build=False):
pim_data = input_dict[dut]["pim"]
config_data = []
if "force_expire" in pim_data:
config_data = []
force_expire_data = pim_data["force_expire"]
for source, groups in force_expire_data.items():
@ -473,17 +460,15 @@ def configure_pim_force_expire(tgen, topo, input_dict, build=False):
)
config_data.append(cmd)
result = create_common_configuration(
tgen, dut, config_data, "pim", build=build
)
if result is not True:
return False
if config_data:
config_data_dict[dut] = config_data
result = create_common_configurations(
tgen, config_data_dict, "pim", build=build
)
except InvalidCLIError:
# Traceback
errormsg = traceback.format_exc()
logger.error(errormsg)
return errormsg
logger.error("configure_pim_force_expire", exc_info=True)
result = False
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result
@ -963,7 +948,7 @@ def verify_join_state_and_timer(tgen, dut, iif, src_address, group_addresses, ex
return True
@retry(retry_timeout=80)
@retry(retry_timeout=120)
def verify_ip_mroutes(
tgen, dut, src_address, group_addresses, iif, oil, return_uptime=False, mwait=0, expected=True
):
@ -2026,6 +2011,7 @@ def add_rp_interfaces_and_pim_config(tgen, topo, interface, rp, rp_mapping):
config_data.append("ip address {}".format(_rp))
config_data.append("ip pim")
# Why not config just once, why per group?
result = create_common_configuration(
tgen, rp, config_data, "interface_config"
)

View File

@ -34,7 +34,7 @@ from lib.topolog import logger
from lib.common_config import (
number_to_row,
number_to_column,
load_config_to_router,
load_config_to_routers,
create_interfaces_cfg,
create_static_routes,
create_prefix_lists,
@ -342,10 +342,8 @@ def build_config_from_json(tgen, topo, save_bkup=True):
func_dict.get(func_type)(tgen, data, build=True)
for router in sorted(topo["routers"].keys()):
logger.debug("Configuring router {}...".format(router))
result = load_config_to_router(tgen, router, save_bkup)
if not result:
logger.info("Failed while configuring {}".format(router))
pytest.exit(1)
routers = sorted(topo["routers"].keys())
result = load_config_to_routers(tgen, routers, save_bkup)
if not result:
logger.info("build_config_from_json: failed to configure topology")
pytest.exit(1)