blob: 1ca79fedcaba8941683a7f16c31111a8248e7b11 [file] [log] [blame]
Dan Talayco48370102010-03-03 15:17:33 -08001#!/usr/bin/env python
2"""
3@package oft
4
5OpenFlow test framework top level script
6
7This script is the entry point for running OpenFlow tests
8using the OFT framework.
9
10The global configuration is passed around in a dictionary
Brandon Heller88f709d2010-04-01 12:29:56 -070011generally called config. The keys have the following
Dan Talayco48370102010-03-03 15:17:33 -080012significance.
13
Dan Talayco2c0dba32010-03-06 22:47:06 -080014<pre>
Dan Talayco48370102010-03-03 15:17:33 -080015 platform : String identifying the target platform
16 controller_host : Host on which test controller is running (for sockets)
17 controller_port : Port on which test controller listens for switch cxn
Dan Talayco2c0dba32010-03-06 22:47:06 -080018 test_dir : (TBD) Directory to search for test files (default .)
Dan Talayco48370102010-03-03 15:17:33 -080019 test_spec : (TBD) Specification of test(s) to run
20 log_file : Filename for test logging
Dan Talayco2c0dba32010-03-06 22:47:06 -080021 list : Boolean: List all tests and exit
Dan Talayco48370102010-03-03 15:17:33 -080022 debug : String giving debug level (info, warning, error...)
Dan Talayco2c0dba32010-03-06 22:47:06 -080023</pre>
Dan Talayco48370102010-03-03 15:17:33 -080024
25See config_defaults below for the default values.
26
Dan Talayco2c0dba32010-03-06 22:47:06 -080027The following are stored in the config dictionary, but are not currently
28configurable through the command line.
29
30<pre>
31 dbg_level : logging module value of debug level
32 port_map : Map of dataplane OpenFlow port to OS interface names
Dan Talayco2c0dba32010-03-06 22:47:06 -080033</pre>
34
Rich Laned1d9c282012-10-04 22:07:10 -070035Each test may be assigned a priority by setting the "priority" property
36in the class definition. For now, the only use of this is to avoid
Dan Talaycoc24aaae2010-07-08 14:05:24 -070037automatic inclusion of tests into the default list. This is done by
Rich Laned1d9c282012-10-04 22:07:10 -070038setting the priority value less than 0. Eventually we may add ordering
Dan Talaycoc24aaae2010-07-08 14:05:24 -070039of test execution by test priority.
40
Dan Talayco2c0dba32010-03-06 22:47:06 -080041To add a test to the system, either: edit an existing test case file (like
42basic.py) to add a test class which inherits from unittest.TestCase (directly
Rich Lane477f4812012-10-04 22:49:00 -070043or indirectly); or add a new file with the test case class. Preferably the
44file is in the same directory as existing tests, though you can specify the
45directory on the command line. The file should not be called "all" as that's
46reserved for the test-spec.
Dan Talayco2c0dba32010-03-06 22:47:06 -080047
48TBD: To add configuration to the system, first add an entry to config_default
Dan Talayco48370102010-03-03 15:17:33 -080049below. If you want this to be a command line parameter, edit config_setup
50to add the option and default value to the parser. Then edit config_get
51to make sure the option value gets copied into the configuration
52structure (which then gets passed to everyone else).
53
54By convention, oft attempts to import the contents of a file by the
55name of $platform.py into the local namespace.
56
57IMPORTANT: That file should define a function platform_config_update which
58takes a configuration dictionary as an argument and updates it for the
59current run. In particular, it should set up config["port_map"] with
60the proper map from OF port numbers to OF interface names.
61
Rich Lane8aebc5e2012-09-25 17:57:53 -070062You can add your own platform, say gp104, by adding a file gp104.py to the
63platforms directory that defines the function platform_config_update and then
64use the parameter --platform=gp104 on the command line. You can also use the
65--platform-dir option to change which directory is searched.
Dan Talayco48370102010-03-03 15:17:33 -080066
Rich Lane477f4812012-10-04 22:49:00 -070067The current model for test sets is basic.py.
Dan Talayco48370102010-03-03 15:17:33 -080068
Dan Talayco52f64442010-03-03 15:32:41 -080069Default setup:
70
71The default setup runs locally using veth pairs. To exercise this,
72checkout and build an openflow userspace datapath. Then start it on
73the local host:
Dan Talayco2c0dba32010-03-06 22:47:06 -080074<pre>
Dan Talayco52f64442010-03-03 15:32:41 -080075 sudo ~/openflow/regress/bin/veth_setup.pl
76 sudo ofdatapath -i veth0,veth2,veth4,veth6 punix:/tmp/ofd &
77 sudo ofprotocol unix:/tmp/ofd tcp:127.0.0.1 --fail=closed --max-backoff=1 &
78
79Next, run oft:
80 sudo ./oft --debug=info
Dan Talayco2c0dba32010-03-06 22:47:06 -080081</pre>
Dan Talayco52f64442010-03-03 15:32:41 -080082
83Examine oft.log if things don't work.
Dan Talayco2c0dba32010-03-06 22:47:06 -080084
Dan Talayco1a88c122010-03-07 22:00:20 -080085@todo Support per-component debug levels (esp controller vs dataplane)
Rich Lane15f64de2012-10-04 21:25:57 -070086@todo Allow specification of priority to override prio check
Dan Talayco2c0dba32010-03-06 22:47:06 -080087
Dan Talayco1a88c122010-03-07 22:00:20 -080088Current test case setup:
Rich Lane477f4812012-10-04 22:49:00 -070089 File with the .py extension in the test directory are considered test files.
90 Support a command line option --test-spec to choose the tests to run.
91 Support test-spec "all" to specify all tests.
Dan Talayco48370102010-03-03 15:17:33 -080092"""
93
94import sys
Rich Lane95f078b2013-01-06 13:24:58 -080095import optparse
Dan Talayco2c0dba32010-03-06 22:47:06 -080096from subprocess import Popen,PIPE
Dan Talayco48370102010-03-03 15:17:33 -080097import logging
98import unittest
Dan Talayco2c0dba32010-03-06 22:47:06 -080099import time
Brandon Heller446c1432010-04-01 12:43:27 -0700100import os
Rich Lane6b452bc2012-07-09 16:52:21 -0700101import imp
Rich Lane8592bec2012-09-03 09:06:59 -0700102import random
Rich Lane5bd6cf92012-10-04 17:57:24 -0700103import signal
Rich Lane943be672012-10-04 19:20:16 -0700104import fnmatch
Dan Talayco48370102010-03-03 15:17:33 -0800105
Rich Lanefadf3452012-10-03 16:23:37 -0700106root_dir = os.path.dirname(os.path.realpath(__file__))
107
108pydir = os.path.join(root_dir, 'src', 'python')
Rich Lane39878042012-07-09 14:45:35 -0700109if os.path.exists(os.path.join(pydir, 'oftest')):
110 # Running from source tree
111 sys.path.insert(0, pydir)
112
Rich Lane477f4812012-10-04 22:49:00 -0700113import oftest
114from oftest import config
115
Rich Laneb0470142012-10-04 15:50:35 -0700116try:
117 import oftest.message
118except:
119 sys.exit("Missing OpenFlow message classes: please run \"make -C tools/munger\"")
120
Rich Laneda3b5ad2012-10-03 09:05:32 -0700121import oftest.testutils
Rich Lanee55abf72012-07-26 20:11:42 -0700122import oftest.ofutils
Rich Lane95f078b2013-01-06 13:24:58 -0800123import oftest.help_formatter
Dan Talaycoba3745c2010-07-21 21:51:08 -0700124
Dan Talayco02eca0b2010-04-15 16:09:43 -0700125try:
126 import scapy.all as scapy
127except:
128 try:
129 import scapy as scapy
130 except:
131 sys.exit("Need to install scapy for packet parsing")
132
Dan Talayco48370102010-03-03 15:17:33 -0800133##@var DEBUG_LEVELS
134# Map from strings to debugging levels
135DEBUG_LEVELS = {
136 'debug' : logging.DEBUG,
137 'verbose' : logging.DEBUG,
138 'info' : logging.INFO,
139 'warning' : logging.WARNING,
140 'warn' : logging.WARNING,
141 'error' : logging.ERROR,
142 'critical' : logging.CRITICAL
143}
144
Dan Talayco48370102010-03-03 15:17:33 -0800145##@var config_default
146# The default configuration dictionary for OFT
147config_default = {
Rich Lane95f078b2013-01-06 13:24:58 -0800148 # Miscellaneous options
149 "list" : False,
150 "list_test_names" : False,
151 "allow_user" : False,
152
153 # Test selection options
Rich Lanec76b09a2013-01-02 16:53:22 -0800154 "test_spec" : "",
Rich Lane1a08d232013-01-04 16:03:50 -0800155 "test_file" : None,
Rich Lane95f078b2013-01-06 13:24:58 -0800156 "test_dir" : os.path.join(root_dir, "tests"),
157
158 # Switch connection options
159 "controller_host" : "0.0.0.0", # For passive bind
160 "controller_port" : 6633,
161 "switch_ip" : None, # If not none, actively connect to switch
162 "platform" : "local",
163 "platform_args" : None,
164 "platform_dir" : os.path.join(root_dir, "platforms"),
165
166 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -0800167 "log_file" : "oft.log",
Dan Talayco69ca4d62012-11-15 11:50:22 -0800168 "log_append" : False,
Rich Lane95f078b2013-01-06 13:24:58 -0800169 "debug" : "verbose",
170
171 # Test behavior options
172 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -0700173 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700174 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -0700175 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000176 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -0700177 "random_seed" : None,
Rich Lane95f078b2013-01-06 13:24:58 -0800178
179 # Other configuration
180 "port_map" : {}, # TODO replace with --interface
Dan Talayco48370102010-03-03 15:17:33 -0800181}
182
Rich Lane95f078b2013-01-06 13:24:58 -0800183def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -0800184 """
185 Set up the configuration including parsing the arguments
186
Dan Talayco48370102010-03-03 15:17:33 -0800187 @return A pair (config, args) where config is an config
188 object and args is any additional arguments from the command line
189 """
190
Rich Lane4113a582013-01-03 10:13:02 -0800191 usage = "usage: %prog [options] (test|group)..."
192
Rich Lane95f078b2013-01-06 13:24:58 -0800193 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800194OFTest is a framework and set of tests for validating OpenFlow switches.
195
196The default configuration assumes that an OpenFlow 1.0 switch is attempting to
197connect to a controller on the machine running OFTest, port 6633. Additionally,
198the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
199dataplane.
200
201If no positional arguments are given then OFTest will run all tests that
202depend only on standard OpenFlow 1.0. Otherwise each positional argument
203is interpreted as either a test name or a test group name. The union of
204these will be executed. To see what groups each test belongs to use the
205--list option.
206"""
207
Rich Lane95f078b2013-01-06 13:24:58 -0800208 parser = optparse.OptionParser(version="%prog 0.1",
209 usage=usage,
210 description=description,
211 formatter=oftest.help_formatter.HelpFormatter())
Dan Talayco48370102010-03-03 15:17:33 -0800212
213 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800214 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800215
Dan Talayco2c0dba32010-03-06 22:47:06 -0800216 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700217 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800218 parser.add_option("--list-test-names", action='store_true',
219 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700220 parser.add_option("--allow-user", action="store_true",
221 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800222
223 group = optparse.OptionGroup(parser, "Test selection options")
224 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
225 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
226 group.add_option("--test-dir", type="string", help="Directory containing tests")
227 parser.add_option_group(group)
228
229 group = optparse.OptionGroup(parser, "Switch connection options")
230 group.add_option("-H", "--host", dest="controller_host",
231 help="IP address to listen on (default %default)")
232 group.add_option("-p", "--port", dest="controller_port",
233 type="int", help="Port number to listen on (default %default)")
234 group.add_option("-S", "--switch-ip", dest="switch_ip",
235 help="If set, actively connect to this switch by IP")
236 group.add_option("-P", "--platform", help="Platform module name (default %default)")
237 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
238 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
239 parser.add_option_group(group)
240
241 group = optparse.OptionGroup(parser, "Logging options")
242 group.add_option("--log-file",
243 help="Name of log file, empty string to log to console (default %default)")
244 group.add_option("--log-append", action="store_true",
245 help="Do not delete log file if specified")
246 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
247 group.add_option("--debug", choices=dbg_lvl_names,
248 help="Debug lvl: debug, info, warning, error, critical (default %default)")
249 group.add_option("-v", "--verbose", action="store_const", dest="debug",
250 const="verbose", help="Shortcut for --debug=verbose")
251 group.add_option("-q", "--quiet", action="store_const", dest="debug",
252 const="warning", help="Shortcut for --debug=warning")
253 parser.add_option_group(group)
254
255 group = optparse.OptionGroup(parser, "Test behavior options")
256 group.add_option("--relax", action="store_true",
257 help="Relax packet match checks allowing other packets")
258 test_params_help = """Set test parameters: key=val;... (see --list)
259 """
260 group.add_option("-t", "--test-params", help=test_params_help)
261 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700262 help="Return failure if any test was skipped")
Rich Lane95f078b2013-01-06 13:24:58 -0800263 group.add_option("--default-timeout", type="int",
Rich Lanee55abf72012-07-26 20:11:42 -0700264 help="Timeout in seconds for most operations")
Rich Lane95f078b2013-01-06 13:24:58 -0800265 group.add_option("--minsize", type="int",
266 help="Minimum allowable packet size on the dataplane.")
267 group.add_option("--random-seed", type="int",
268 help="Random number generator seed")
269 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000270
Dan Talayco48370102010-03-03 15:17:33 -0800271 # Might need this if other parsers want command line
272 # parser.allow_interspersed_args = False
273 (options, args) = parser.parse_args()
274
Rich Lane95f078b2013-01-06 13:24:58 -0800275 # Convert options from a Namespace to a plain dictionary
276 config = config_default.copy()
277 for key in config.keys():
278 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800279
280 return (config, args)
281
282def logging_setup(config):
283 """
284 Set up logging based on config
285 """
Rich Lane71d887d2012-12-22 17:05:13 -0800286 _format = "%(asctime)s.%(msecs)d %(name)-10s: %(levelname)-8s: %(message)s"
Dan Talayco48370102010-03-03 15:17:33 -0800287 _datefmt = "%H:%M:%S"
Dan Talayco69ca4d62012-11-15 11:50:22 -0800288 _mode = config["log_append"] and "a" or "w"
Dan Talayco88fc8802010-03-07 11:37:52 -0800289 logging.basicConfig(filename=config["log_file"],
Dan Talayco69ca4d62012-11-15 11:50:22 -0800290 filemode=_mode,
Rich Lane95f078b2013-01-06 13:24:58 -0800291 level=DEBUG_LEVELS[config["debug"]],
Dan Talayco88fc8802010-03-07 11:37:52 -0800292 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800293
Rich Lane943be672012-10-04 19:20:16 -0700294def load_test_modules(config):
295 """
296 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800297
Rich Lane943be672012-10-04 19:20:16 -0700298 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800299
Rich Lanecc45b8e2013-01-02 15:55:02 -0800300 Also updates the _groups member to include "standard" and
301 module test groups if appropriate.
302
Dan Talayco2c0dba32010-03-06 22:47:06 -0800303 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700304 @returns A dictionary from test module names to tuples of
305 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800306 """
307
Rich Lane943be672012-10-04 19:20:16 -0700308 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800309
Rich Lane943be672012-10-04 19:20:16 -0700310 for root, dirs, filenames in os.walk(config["test_dir"]):
311 # Iterate over each python file
312 for filename in fnmatch.filter(filenames, '[!.]*.py'):
313 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800314
Rich Lane943be672012-10-04 19:20:16 -0700315 try:
316 if sys.modules.has_key(modname):
317 mod = sys.modules[modname]
318 else:
319 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
320 except:
321 logging.warning("Could not import file " + filename)
322 raise
Rich Lane520e4152012-07-09 16:18:16 -0700323
Rich Lane943be672012-10-04 19:20:16 -0700324 # Find all testcases defined in the module
325 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
326 issubclass(v, unittest.TestCase))
327 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800328 for (testname, test) in tests.items():
329 # Set default annotation values
330 if not hasattr(test, "_groups"):
331 test._groups = []
332 if not hasattr(test, "_nonstandard"):
333 test._nonstandard = False
334 if not hasattr(test, "_disabled"):
335 test._disabled = False
336
337 # Put test in its module's test group
338 if not test._disabled:
339 test._groups.append(modname)
340
341 # Put test in the standard test group
342 if not test._disabled and not test._nonstandard:
343 test._groups.append("standard")
344 test._groups.append("all") # backwards compatibility
345
Rich Lane943be672012-10-04 19:20:16 -0700346 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700347
Rich Lane943be672012-10-04 19:20:16 -0700348 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800349
Rich Lanec76b09a2013-01-02 16:53:22 -0800350def prune_tests(test_specs, test_modules):
Rich Lane15f64de2012-10-04 21:25:57 -0700351 """
Rich Lanec76b09a2013-01-02 16:53:22 -0800352 Return tests matching the given test-specs
353 @param test_specs A list of group names or test names.
Rich Lane15f64de2012-10-04 21:25:57 -0700354 @param test_modules Same format as the output of load_test_modules.
355 @returns Same format as the output of load_test_modules.
356 """
357 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800358 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700359 matched = False
360 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800361 for (testname, test) in tests.items():
362 if e in test._groups or e == "%s.%s" % (modname, testname):
363 result.setdefault(modname, (mod, {}))
364 result[modname][1][testname] = test
365 matched = True
Rich Lane15f64de2012-10-04 21:25:57 -0700366 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800367 die("test-spec element %s did not match any tests" % e)
Rich Lane15f64de2012-10-04 21:25:57 -0700368 return result
369
Dan Talayco2c0dba32010-03-06 22:47:06 -0800370def die(msg, exit_val=1):
371 print msg
372 logging.critical(msg)
373 sys.exit(exit_val)
374
Dan Talayco79f36082010-03-11 16:53:53 -0800375def _space_to(n, str):
376 """
377 Generate a string of spaces to achieve width n given string str
378 If length of str >= n, return one space
379 """
380 spaces = n - len(str)
381 if spaces > 0:
382 return " " * spaces
383 return " "
384
Dan Talayco48370102010-03-03 15:17:33 -0800385#
386# Main script
387#
388
Rich Lane477f4812012-10-04 22:49:00 -0700389# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800390(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700391oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800392
Rich Laned7a85c42012-09-28 15:38:45 -0700393logging_setup(config)
394logging.info("++++++++ " + time.asctime() + " ++++++++")
395
Rich Lanee284b6b2012-10-03 09:19:58 -0700396# Allow tests to import each other
397sys.path.append(config["test_dir"])
398
Rich Lanec76b09a2013-01-02 16:53:22 -0800399test_specs = args
400if config["test_spec"] != "":
401 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
402 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800403if config["test_file"] != None:
404 with open(config["test_file"], 'r') as f:
405 for line in f:
406 line, _, _ = line.partition('#') # remove comments
407 line = line.strip()
408 if line:
409 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800410if test_specs == []:
411 test_specs = ["standard"]
412
Rich Laned8e45482013-01-02 17:36:21 -0800413test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800414
415# Check if test list is requested; display and exit if so
416if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700417 mod_count = 0
418 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800419 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800420 print """\
421Tests are shown grouped by module. If a test is in any groups beyond "standard"
422and its module's group then they are shown in parentheses."""
423 print
424 print """\
425Tests marked with '*' are non-standard and may require vendor extensions or
426special switch configuration. These are not part of the "standard" test group."""
427 print
428 print """\
429Tests marked with '!' are disabled because they are experimental, special-purpose,
430or are too long to be run normally. These are not part of the "standard" test
431group or their module's test group."""
432 print
433 print "Tests marked (TP1) after name take --test-params including:"
434 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800435 print
436 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700437 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700438 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800439 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800440 start_str = " Module " + mod.__name__ + ": "
441 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700442 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700443 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800444 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700445 desc = desc.split('\n')[0]
446 except:
447 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800448 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800449 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800450 if groups:
451 desc = "(%s) %s" % (",".join(groups), desc)
452 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
453 test._disabled and "!" or " ",
454 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700455 if len(start_str) > 22:
456 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800457 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700458 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800459 print
Rich Lane15f64de2012-10-04 21:25:57 -0700460 print "%d modules shown with a total of %d tests" % \
461 (mod_count, test_count)
462 print
Rich Lane37f42112013-01-03 13:41:49 -0800463 print "Test groups: %s" % (', '.join(sorted(all_groups)))
464
Dan Talayco2c0dba32010-03-06 22:47:06 -0800465 sys.exit(0)
466
Rich Laned8e45482013-01-02 17:36:21 -0800467test_modules = prune_tests(test_specs, test_modules)
468
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700469# Check if test list is requested; display and exit if so
470if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700471 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700472 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800473 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700474 sys.exit(0)
475
Dan Talayco2c0dba32010-03-06 22:47:06 -0800476# Generate the test suite
477#@todo Decide if multiple suites are ever needed
478suite = unittest.TestSuite()
479
Rich Lane15f64de2012-10-04 21:25:57 -0700480for (modname, (mod, tests)) in test_modules.items():
481 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800482 logging.info("Adding test " + modname + "." + testname)
483 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800484
Rich Lane51590f62012-10-09 15:06:29 -0700485# Allow platforms to import each other
486sys.path.append(config["platform_dir"])
487
Rich Lane8aebc5e2012-09-25 17:57:53 -0700488# Load the platform module
489platform_name = config["platform"]
490logging.info("Importing platform: " + platform_name)
491platform_mod = None
492try:
Rich Lane483e1542012-10-05 09:29:39 -0700493 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700494except:
495 logging.warn("Failed to import " + platform_name + " platform module")
496 raise
Dan Talayco48370102010-03-03 15:17:33 -0800497
498try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700499 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800500except:
501 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700502 raise
Dan Talayco48370102010-03-03 15:17:33 -0800503
504if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700505 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800506
507logging.debug("Configuration: " + str(config))
508logging.info("OF port map: " + str(config["port_map"]))
509
Rich Lanee55abf72012-07-26 20:11:42 -0700510oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700511oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700512
Rich Laneee57ad02012-07-13 15:40:36 -0700513if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700514 print "ERROR: Super-user privileges required. Please re-run with " \
515 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700516 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700517
Rich Lane8592bec2012-09-03 09:06:59 -0700518if config["random_seed"] is not None:
519 logging.info("Random seed: %d" % config["random_seed"])
520 random.seed(config["random_seed"])
521
Rich Lane5bd6cf92012-10-04 17:57:24 -0700522# Remove python's signal handler which raises KeyboardError. Exiting from an
523# exception waits for all threads to terminate which might not happen.
524signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700525
526if __name__ == "__main__":
Rich Lane2c7812c2012-12-27 17:52:23 -0800527 # Set up the dataplane
528 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
529 for of_port, ifname in config["port_map"].items():
530 oftest.dataplane_instance.port_add(ifname, of_port)
531
Dan Talaycoac25cf32010-07-20 14:08:28 -0700532 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800533 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700534 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700535 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700536 if oftest.testutils.skipped_test_count == 1: ts = " test"
537 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
538 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700539 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800540
541 # Shutdown the dataplane
542 oftest.dataplane_instance.kill()
543 oftest.dataplane_instance = None
544
Rich Lane50d42eb2012-07-16 11:57:03 -0700545 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700546 # exit(1) hangs sometimes
547 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700548 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700549 os._exit(1)