]> git.puffer.fish Git - matthieu/frr.git/commitdiff
tests: Adds basic config load and interface API
authorAshish Pant <ashish12pant@gmail.com>
Mon, 24 Jun 2019 11:37:00 +0000 (17:07 +0530)
committerAshish Pant <ashish12pant@gmail.com>
Tue, 9 Jul 2019 04:56:53 +0000 (10:26 +0530)
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

tests/topotests/lib/common_config.py
tests/topotests/lib/topojson.py
tests/topotests/pytest.ini

index f043df2b0639ba0478712559477b5e90ed5a1c8b..ebb81c465355c3e9612d4df5e5b1806f8df2bd41 100644 (file)
 # 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
+
index b76fb610677188de5dfc31aea8580aa277a3979f..36dff6b98e85f49e31fce03d37557cee302255b0 100644 (file)
 # 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)
+
index 119ab93857edd8dbde48a1bae884815d71ad902d..7ea38491d82892cb7c0e3db3c0c4b227f1dbd61d 100644 (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