Skip to content

Commit

Permalink
Merge pull request #11 from opensourcerouting/tgen-config
Browse files Browse the repository at this point in the history
Topogen Configuration File
  • Loading branch information
mwinter-osr authored Jul 5, 2017
2 parents 187f36a + 421cb2d commit 39e371f
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 11 deletions.
19 changes: 18 additions & 1 deletion GUIDELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down Expand Up @@ -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
```
48 changes: 44 additions & 4 deletions lib/topogen.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,16 @@
import os
import sys
import json
import ConfigParser

from mininet.net import Mininet
from mininet.log import setLogLevel
from mininet.cli import CLI

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.
Expand Down Expand Up @@ -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 = {}
Expand All @@ -97,25 +103,52 @@ 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()
self.net = Mininet(controller=None, topo=self.topo)
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:
name = 'r{}'.format(self.routern)
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]
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down
31 changes: 25 additions & 6 deletions lib/topotest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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')
Expand Down
24 changes: 24 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -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 =

0 comments on commit 39e371f

Please sign in to comment.