diff --git a/GUIDELINES.md b/GUIDELINES.md index 1566705bc1b8..aeec70f8be6d 100644 --- a/GUIDELINES.md +++ b/GUIDELINES.md @@ -44,8 +44,13 @@ r1-zebra.out # zebra stdout output You can also run memory leak tests to get reports: ```shell +$ # Set the environment variable to apply to a specific test... $ sudo env TOPOTESTS_CHECK_MEMLEAK="/tmp/memleak_report_" pytest ospf-topo1/test_ospf_topo1.py -... +$ # ...or apply to all tests adding this line to the configuration file +$ echo 'memleak_path = /tmp/memleak_report_' >> pytest.ini +$ # You can also use your editor +$ $EDITOR pytest.ini +$ # After running tests you should see your files: $ ls /tmp/memleak_report_* memleak_report_test_ospf_topo1.txt ``` @@ -552,3 +557,15 @@ r1 *** Starting CLI: mininet> ``` + +To enable more debug messages in other Topogen subsystems (like Mininet), more +logging messages can be displayed by modifying the test configuration file +`pytest.ini`: + +```ini +[topogen] +# Change the default verbosity line from 'info'... +#verbosity = info +# ...to 'debug' +verbosity = debug +``` diff --git a/lib/topogen.py b/lib/topogen.py index 97f006c59cda..2b2ea5dcb555 100644 --- a/lib/topogen.py +++ b/lib/topogen.py @@ -41,6 +41,7 @@ import os import sys import json +import ConfigParser from mininet.net import Mininet from mininet.log import setLogLevel @@ -48,6 +49,8 @@ from lib import topotest +CWD = os.path.dirname(os.path.realpath(__file__)) + # pylint: disable=C0103 # Global Topogen variable. This is being used to keep the Topogen available on # all test functions without declaring a test local variable. @@ -75,7 +78,10 @@ def set_topogen(tgen): class Topogen(object): "A topology test builder helper." + CONFIG_SECTION = 'topogen' + def __init__(self, cls): + self.config = None self.topo = None self.net = None self.gears = {} @@ -97,6 +103,9 @@ def _init_topo(self, cls): # Set the global variable so the test cases can access it anywhere set_topogen(self) + # Load the default topology configurations + self._load_config() + # Initialize the API self._mininet_reset() cls() @@ -104,11 +113,29 @@ def _init_topo(self, cls): for gear in self.gears.values(): gear.net = self.net + def _load_config(self): + """ + Loads the configuration file `pytest.ini` located at the root dir of + topotests. + """ + defaults = { + 'verbosity': 'info', + 'frrdir': '/usr/lib/frr', + 'quaggadir': '/usr/lib/quagga', + 'routertype': 'frr', + 'memleak_path': None, + } + self.config = ConfigParser.ConfigParser(defaults) + pytestini_path = os.path.join(CWD, '../pytest.ini') + self.config.read(pytestini_path) + def add_router(self, name=None, cls=topotest.Router, **params): """ Adds a new router to the topology. This function has the following options: - name: (optional) select the router name + * `name`: (optional) select the router name + * `daemondir`: (optional) custom daemon binary directory + * `routertype`: (optional) `quagga` or `frr` Returns a TopoRouter. """ if name is None: @@ -116,6 +143,12 @@ def add_router(self, name=None, cls=topotest.Router, **params): if name in self.gears: raise KeyError('router already exists') + params['frrdir'] = self.config.get(self.CONFIG_SECTION, 'frrdir') + params['quaggadir'] = self.config.get(self.CONFIG_SECTION, 'quaggadir') + params['memleak_path'] = self.config.get(self.CONFIG_SECTION, 'memleak_path') + if not params.has_key('routertype'): + params['routertype'] = self.config.get(self.CONFIG_SECTION, 'routertype') + self.gears[name] = TopoRouter(self, cls, name, **params) self.routern += 1 return self.gears[name] @@ -167,7 +200,7 @@ def routers(self): return dict((rname, gear) for rname, gear in self.gears.iteritems() if isinstance(gear, TopoRouter)) - def start_topology(self, log_level='info'): + def start_topology(self, log_level=None): """ Starts the topology class. Possible `log_level`s are: 'debug': all information possible @@ -177,6 +210,10 @@ def start_topology(self, log_level='info'): 'error': only error and critical messages 'critical': only critical messages """ + # If log_level is not specified use the configuration. + if log_level is None: + log_level = self.config.get('topogen', 'verbosity') + # Run mininet setLogLevel(log_level) self.net.start() @@ -353,8 +390,11 @@ def __init__(self, tgen, cls, name, **params): self.net = None self.name = name self.cls = cls + self.options = {} if not params.has_key('privateDirs'): params['privateDirs'] = self.PRIVATE_DIRS + + self.options['memleak_path'] = params.get('memleak_path', None) self.tgen.topo.addNode(self.name, cls=self.cls, **params) def __str__(self): @@ -437,9 +477,9 @@ def report_memory_leaks(self, testname): testname: the test file name for identification NOTE: to run this you must have the environment variable - TOPOTESTS_CHECK_MEMLEAK set to the appropriated path. + TOPOTESTS_CHECK_MEMLEAK set or memleak_path configured in `pytest.ini`. """ - memleak_file = os.environ.get('TOPOTESTS_CHECK_MEMLEAK') + memleak_file = os.environ.get('TOPOTESTS_CHECK_MEMLEAK') or self.options['memleak_path'] if memleak_file is None: print "SKIPPED check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)" return diff --git a/lib/topotest.py b/lib/topotest.py index 9f540fc86f3b..da7dc06191c3 100644 --- a/lib/topotest.py +++ b/lib/topotest.py @@ -259,6 +259,26 @@ def __init__(self, name, **params): 'ospf6d': 0, 'isisd': 0, 'bgpd': 0, 'pimd': 0, 'ldpd': 0} + def _config_frr(self, **params): + "Configure FRR binaries" + self.daemondir = params.get('frrdir') + if self.daemondir is None: + self.daemondir = '/usr/lib/frr' + + zebra_path = os.path.join(self.daemondir, 'zebra') + if not os.path.isfile(zebra_path): + raise Exception("FRR zebra binary doesn't exist at {}".format(zebra_path)) + + def _config_quagga(self, **params): + "Configure Quagga binaries" + self.daemondir = params.get('quaggadir') + if self.daemondir is None: + self.daemondir = '/usr/lib/quagga' + + zebra_path = os.path.join(self.daemondir, 'zebra') + if not os.path.isfile(zebra_path): + raise Exception("Quagga zebra binary doesn't exist at {}".format(zebra_path)) + # pylint: disable=W0221 # Some params are only meaningful for the parent class. def config(self, **params): @@ -267,12 +287,11 @@ def config(self, **params): # User did not specify the daemons directory, try to autodetect it. self.daemondir = params.get('daemondir') if self.daemondir is None: - self.daemondir = '/usr/lib/frr' - if not os.path.isfile(os.path.join(self.daemondir, 'zebra')): - self.daemondir = '/usr/lib/quagga' - self.routertype = 'quagga' - if not os.path.isfile(os.path.join(self.daemondir, 'zebra')): - raise Exception('No FRR or Quagga binaries found') + self.routertype = params.get('routertype', 'frr') + if self.routertype == 'quagga': + self._config_quagga(**params) + else: + self._config_frr(**params) else: # Test the provided path zpath = os.path.join(self.daemondir, 'zebra') diff --git a/pytest.ini b/pytest.ini index a744b34aae74..9dbe3834c243 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,3 +1,27 @@ # Skip pytests example directory [pytest] norecursedirs = .git example-test + +[topogen] +# Default configuration values +# +# 'verbosity' controls how much data the underline systems will use to +# provide output (e.g. mininet output, test debug output etc...). The +# value is 'info', but can be changed to 'debug' to provide more details. +#verbosity = info + +# Default daemons binaries path. +#frrdir = /usr/lib/frr +#quaggadir = /usr/lib/quagga + +# Default router type to use. Possible values are: +# 'frr' and 'quagga'. +#routertype = frr + +# Memory leak test reports path +# Enables and add an output path to memory leak tests. +# Example: +# memleak_path = /tmp/memleak_ +# Output files will be named after the testname: +# /tmp/memleak_test_ospf_topo1.txt +#memleak_path =