tests: Adds basic config load and interface API

Signed-off-by: Ashish Pant <ashish12pant@gmail.com>

Adds method to load configuration on the router. Save configuration
for resetting.
Adds method for configuring interfaces ip address on router.
Adds logdir and other pytest.ini option
This commit is contained in:
Ashish Pant 2019-06-24 17:07:00 +05:30
parent 27339562f8
commit 7659b8d4e4
3 changed files with 247 additions and 2 deletions

View File

@ -18,12 +18,25 @@
# OF THIS SOFTWARE.
#
from collections import OrderedDict
from datetime import datetime
import os
import ConfigParser
import traceback
from lib.topolog import logger, logger_config
from lib.topogen import TopoRouter
FRRCFG_FILE = "frr_json.conf"
FRRCFG_BKUP_FILE = "frr_json_initial.conf"
ERROR_LIST = ["Malformed", "Failure", "Unknown"]
####
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
@ -31,6 +44,143 @@ from lib.topogen import TopoRouter
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()
config.read(PYTESTINI_PATH)
config_section = "topogen"
if config.has_option("topogen", "verbosity"):
loglevel = config.get("topogen", "verbosity")
loglevel = loglevel.upper()
else:
loglevel = "INFO"
if config.has_option("topogen", "frrtest_log_dir"):
frrtest_log_dir = config.get("topogen", "frrtest_log_dir")
time_stamp = datetime.time(datetime.now())
logfile_name = "frr_test_bgp_"
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)
print("Logs will be sent to logfile: {}".format(frrtest_log_file))
if config.has_option("topogen", "show_router_config"):
show_router_config = config.get("topogen", "show_router_config")
else:
show_router_config = False
class InvalidCLIError(Exception):
"""Raise when the CLI command is wrong"""
pass
def create_common_configuration(tgen, router, data, config_type=None,
build=False):
"""
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 onject
* `data`: Congiguration 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
"""
TMPDIR = os.path.join(LOGDIR, tgen.modname)
fname = "{}/{}/{}".format(TMPDIR, router, FRRCFG_FILE)
config_map = OrderedDict({
"general_config": "! FRR General Config\n",
"interface_config": "! Interfaces Config\n"
})
if build:
mode = "a"
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)))
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()
# If configuration applied from build, it will done at last
if not build:
load_config_to_router(tgen, router)
return True
def load_config_to_router(tgen, routerName, save_bkup=False):
"""
Loads configuration on router from the file FRRCFG_FILE.
Parameters
----------
* `tgen` : Topogen object
* `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, router in router_list.iteritems():
if rname == routerName:
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()
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)
except IOError as err:
errormsg = ("Unable to open config File. error(%s):"
" %s", (err.errno, err.strerror))
return errormsg
logger.info("New configuration for router {}:".format(rname))
new_config = router.run("vtysh -c 'show running'")
# Router current configuration to log file or console if
# "show_router_config" is defined in "pytest.ini"
if show_router_config:
logger.info(new_config)
logger.debug("Exting API: load_config_to_router")
return True
def start_topology(tgen):
"""
@ -120,3 +270,58 @@ def number_to_column(routerName):
"""
return ord(routerName[0]) - 97
#############################################
# These APIs, will used by testcase
#############################################
def create_interfaces_cfg(tgen, topo, build=False):
"""
Create interface configuration for created topology. Basic Interface
configuration is provided in input json file.
Parameters
----------
* `tgen` : Topogen object
* `topo` : json file data
* `build` : Only for initial setup phase this is set as True.
Returns
-------
True or False
"""
result = False
try:
for c_router, c_data in topo.iteritems():
interface_data = []
for destRouterLink, data in sorted(c_data["links"].iteritems()):
# Loopback interfaces
if "type" in data and data["type"] == "loopback":
interface_name = destRouterLink
else:
interface_name = data["interface"]
interface_data.append("interface {}\n".format(
str(interface_name)
))
if "ipv4" in data:
intf_addr = c_data["links"][destRouterLink]["ipv4"]
interface_data.append("ip address {}\n".format(
intf_addr
))
if "ipv6" in data:
intf_addr = c_data["links"][destRouterLink]["ipv6"]
interface_data.append("ipv6 address {}\n".format(
intf_addr
))
result = create_common_configuration(tgen, c_router,
interface_data,
"interface_config",
build=build)
except InvalidCLIError:
# Traceback
errormsg = traceback.format_exc()
logger.error(errormsg)
return errormsg
return result

View File

@ -18,15 +18,20 @@
# OF THIS SOFTWARE.
#
from collections import OrderedDict
from json import dumps as json_dumps
import ipaddr
import pytest
# Import topogen and topotest helpers
from lib.topolog import logger
# Required to instantiate the topology builder class.
from lib.common_config import (number_to_row, number_to_column)
from lib.common_config import (
number_to_row, number_to_column,
load_config_to_router,
create_interfaces_cfg
)
def build_topo_from_json(tgen, topo):
@ -149,3 +154,31 @@ def build_topo_from_json(tgen, topo):
logger.debug("Generated link data for router: %s\n%s", curRouter,
json_dumps(topo["routers"][curRouter]["links"],
indent=4, sort_keys=True)
def build_config_from_json(tgen, topo, save_bkup=True):
"""
Reads initial configuraiton from JSON for each router, builds
configuration and loads its to router.
* `tgen`: Topogen object
* `topo`: json file data
"""
func_dict = OrderedDict([
("links", create_interfaces_cfg),
])
data = topo["routers"]
for func_type in func_dict.keys():
logger.info('Building configuration for {}'.format(func_type))
func_dict.get(func_type)(tgen, data, build=True)
for router in sorted(topo['routers'].keys()):
logger.info('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)

View File

@ -10,6 +10,13 @@ norecursedirs = .git example-test lib docker
# value is 'info', but can be changed to 'debug' to provide more details.
#verbosity = info
# Save logs to log file, by default logs will be displayed to console
#frrtest_log_dir = /tmp/topotests/
# Display router current configuration during test execution,
# by default configuration will not be shown
show_router_config = True
# Default daemons binaries path.
#frrdir = /usr/lib/frr
#quaggadir = /usr/lib/quagga