mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-15 01:37:53 +00:00
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:
parent
27339562f8
commit
7659b8d4e4
@ -18,12 +18,25 @@
|
|||||||
# OF THIS SOFTWARE.
|
# OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
from datetime import datetime
|
||||||
import os
|
import os
|
||||||
|
import ConfigParser
|
||||||
|
import traceback
|
||||||
|
|
||||||
from lib.topolog import logger, logger_config
|
from lib.topolog import logger, logger_config
|
||||||
from lib.topogen import TopoRouter
|
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
|
# Creating tmp dir with testsuite name to avoid conflict condition when
|
||||||
# multiple testsuites run together. All temporary files would be created
|
# multiple testsuites run together. All temporary files would be created
|
||||||
# in this dir and this dir would be removed once testsuite run is
|
# 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/"
|
LOGDIR = "/tmp/topotests/"
|
||||||
TMPDIR = None
|
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):
|
def start_topology(tgen):
|
||||||
"""
|
"""
|
||||||
@ -120,3 +270,58 @@ def number_to_column(routerName):
|
|||||||
"""
|
"""
|
||||||
return ord(routerName[0]) - 97
|
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
|
||||||
|
|
||||||
|
@ -18,15 +18,20 @@
|
|||||||
# OF THIS SOFTWARE.
|
# OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
from json import dumps as json_dumps
|
from json import dumps as json_dumps
|
||||||
import ipaddr
|
import ipaddr
|
||||||
|
import pytest
|
||||||
|
|
||||||
# Import topogen and topotest helpers
|
# Import topogen and topotest helpers
|
||||||
from lib.topolog import logger
|
from lib.topolog import logger
|
||||||
|
|
||||||
# Required to instantiate the topology builder class.
|
# 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):
|
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,
|
logger.debug("Generated link data for router: %s\n%s", curRouter,
|
||||||
json_dumps(topo["routers"][curRouter]["links"],
|
json_dumps(topo["routers"][curRouter]["links"],
|
||||||
indent=4, sort_keys=True)
|
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)
|
||||||
|
|
||||||
|
@ -10,6 +10,13 @@ norecursedirs = .git example-test lib docker
|
|||||||
# value is 'info', but can be changed to 'debug' to provide more details.
|
# value is 'info', but can be changed to 'debug' to provide more details.
|
||||||
#verbosity = info
|
#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.
|
# Default daemons binaries path.
|
||||||
#frrdir = /usr/lib/frr
|
#frrdir = /usr/lib/frr
|
||||||
#quaggadir = /usr/lib/quagga
|
#quaggadir = /usr/lib/quagga
|
||||||
|
Loading…
Reference in New Issue
Block a user