blob: 2fb60d40dcd9907bd904d53b06809c19b02fab46 [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
Rich Laneea5060d2013-01-06 13:59:00 -08007This script is the entry point for running OpenFlow tests using the OFT
8framework. For usage information, see --help or the README.
Dan Talayco48370102010-03-03 15:17:33 -08009
Rich Laneea5060d2013-01-06 13:59:00 -080010To add a new command line option, edit both the config_default dictionary and
11the config_setup function. The option's result will end up in the global
12oftest.config dictionary.
Dan Talayco48370102010-03-03 15:17:33 -080013"""
14
15import sys
Rich Lane95f078b2013-01-06 13:24:58 -080016import optparse
Dan Talayco2c0dba32010-03-06 22:47:06 -080017from subprocess import Popen,PIPE
Dan Talayco48370102010-03-03 15:17:33 -080018import logging
19import unittest
Dan Talayco2c0dba32010-03-06 22:47:06 -080020import time
Brandon Heller446c1432010-04-01 12:43:27 -070021import os
Rich Lane6b452bc2012-07-09 16:52:21 -070022import imp
Rich Lane8592bec2012-09-03 09:06:59 -070023import random
Rich Lane5bd6cf92012-10-04 17:57:24 -070024import signal
Rich Lane943be672012-10-04 19:20:16 -070025import fnmatch
Dan Talayco48370102010-03-03 15:17:33 -080026
Rich Lanefadf3452012-10-03 16:23:37 -070027root_dir = os.path.dirname(os.path.realpath(__file__))
28
29pydir = os.path.join(root_dir, 'src', 'python')
Rich Lane39878042012-07-09 14:45:35 -070030if os.path.exists(os.path.join(pydir, 'oftest')):
31 # Running from source tree
32 sys.path.insert(0, pydir)
33
Rich Lane477f4812012-10-04 22:49:00 -070034import oftest
35from oftest import config
36
Rich Laneb0470142012-10-04 15:50:35 -070037try:
38 import oftest.message
39except:
40 sys.exit("Missing OpenFlow message classes: please run \"make -C tools/munger\"")
41
Rich Laneda3b5ad2012-10-03 09:05:32 -070042import oftest.testutils
Rich Lanee55abf72012-07-26 20:11:42 -070043import oftest.ofutils
Rich Lane95f078b2013-01-06 13:24:58 -080044import oftest.help_formatter
Dan Talaycoba3745c2010-07-21 21:51:08 -070045
Dan Talayco02eca0b2010-04-15 16:09:43 -070046try:
47 import scapy.all as scapy
48except:
49 try:
50 import scapy as scapy
51 except:
52 sys.exit("Need to install scapy for packet parsing")
53
Dan Talayco48370102010-03-03 15:17:33 -080054##@var DEBUG_LEVELS
55# Map from strings to debugging levels
56DEBUG_LEVELS = {
57 'debug' : logging.DEBUG,
58 'verbose' : logging.DEBUG,
59 'info' : logging.INFO,
60 'warning' : logging.WARNING,
61 'warn' : logging.WARNING,
62 'error' : logging.ERROR,
63 'critical' : logging.CRITICAL
64}
65
Dan Talayco48370102010-03-03 15:17:33 -080066##@var config_default
67# The default configuration dictionary for OFT
68config_default = {
Rich Lane95f078b2013-01-06 13:24:58 -080069 # Miscellaneous options
70 "list" : False,
71 "list_test_names" : False,
72 "allow_user" : False,
73
74 # Test selection options
Rich Lanec76b09a2013-01-02 16:53:22 -080075 "test_spec" : "",
Rich Lane1a08d232013-01-04 16:03:50 -080076 "test_file" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080077 "test_dir" : os.path.join(root_dir, "tests"),
78
79 # Switch connection options
80 "controller_host" : "0.0.0.0", # For passive bind
81 "controller_port" : 6633,
82 "switch_ip" : None, # If not none, actively connect to switch
83 "platform" : "local",
84 "platform_args" : None,
85 "platform_dir" : os.path.join(root_dir, "platforms"),
86
87 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -080088 "log_file" : "oft.log",
Dan Talayco69ca4d62012-11-15 11:50:22 -080089 "log_append" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080090 "debug" : "verbose",
91
92 # Test behavior options
93 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070094 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070095 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -070096 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000097 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070098 "random_seed" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080099
100 # Other configuration
101 "port_map" : {}, # TODO replace with --interface
Dan Talayco48370102010-03-03 15:17:33 -0800102}
103
Rich Lane95f078b2013-01-06 13:24:58 -0800104def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -0800105 """
106 Set up the configuration including parsing the arguments
107
Dan Talayco48370102010-03-03 15:17:33 -0800108 @return A pair (config, args) where config is an config
109 object and args is any additional arguments from the command line
110 """
111
Rich Lane4113a582013-01-03 10:13:02 -0800112 usage = "usage: %prog [options] (test|group)..."
113
Rich Lane95f078b2013-01-06 13:24:58 -0800114 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800115OFTest is a framework and set of tests for validating OpenFlow switches.
116
117The default configuration assumes that an OpenFlow 1.0 switch is attempting to
118connect to a controller on the machine running OFTest, port 6633. Additionally,
119the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
120dataplane.
121
122If no positional arguments are given then OFTest will run all tests that
123depend only on standard OpenFlow 1.0. Otherwise each positional argument
124is interpreted as either a test name or a test group name. The union of
125these will be executed. To see what groups each test belongs to use the
126--list option.
127"""
128
Rich Lane95f078b2013-01-06 13:24:58 -0800129 parser = optparse.OptionParser(version="%prog 0.1",
130 usage=usage,
131 description=description,
132 formatter=oftest.help_formatter.HelpFormatter())
Dan Talayco48370102010-03-03 15:17:33 -0800133
134 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800135 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800136
Dan Talayco2c0dba32010-03-06 22:47:06 -0800137 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700138 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800139 parser.add_option("--list-test-names", action='store_true',
140 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700141 parser.add_option("--allow-user", action="store_true",
142 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800143
144 group = optparse.OptionGroup(parser, "Test selection options")
145 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
146 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
147 group.add_option("--test-dir", type="string", help="Directory containing tests")
148 parser.add_option_group(group)
149
150 group = optparse.OptionGroup(parser, "Switch connection options")
151 group.add_option("-H", "--host", dest="controller_host",
152 help="IP address to listen on (default %default)")
153 group.add_option("-p", "--port", dest="controller_port",
154 type="int", help="Port number to listen on (default %default)")
155 group.add_option("-S", "--switch-ip", dest="switch_ip",
156 help="If set, actively connect to this switch by IP")
157 group.add_option("-P", "--platform", help="Platform module name (default %default)")
158 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
159 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
160 parser.add_option_group(group)
161
162 group = optparse.OptionGroup(parser, "Logging options")
163 group.add_option("--log-file",
164 help="Name of log file, empty string to log to console (default %default)")
165 group.add_option("--log-append", action="store_true",
166 help="Do not delete log file if specified")
167 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
168 group.add_option("--debug", choices=dbg_lvl_names,
169 help="Debug lvl: debug, info, warning, error, critical (default %default)")
170 group.add_option("-v", "--verbose", action="store_const", dest="debug",
171 const="verbose", help="Shortcut for --debug=verbose")
172 group.add_option("-q", "--quiet", action="store_const", dest="debug",
173 const="warning", help="Shortcut for --debug=warning")
174 parser.add_option_group(group)
175
176 group = optparse.OptionGroup(parser, "Test behavior options")
177 group.add_option("--relax", action="store_true",
178 help="Relax packet match checks allowing other packets")
179 test_params_help = """Set test parameters: key=val;... (see --list)
180 """
181 group.add_option("-t", "--test-params", help=test_params_help)
182 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700183 help="Return failure if any test was skipped")
Rich Lane95f078b2013-01-06 13:24:58 -0800184 group.add_option("--default-timeout", type="int",
Rich Lanee55abf72012-07-26 20:11:42 -0700185 help="Timeout in seconds for most operations")
Rich Lane95f078b2013-01-06 13:24:58 -0800186 group.add_option("--minsize", type="int",
187 help="Minimum allowable packet size on the dataplane.")
188 group.add_option("--random-seed", type="int",
189 help="Random number generator seed")
190 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000191
Dan Talayco48370102010-03-03 15:17:33 -0800192 # Might need this if other parsers want command line
193 # parser.allow_interspersed_args = False
194 (options, args) = parser.parse_args()
195
Rich Lane95f078b2013-01-06 13:24:58 -0800196 # Convert options from a Namespace to a plain dictionary
197 config = config_default.copy()
198 for key in config.keys():
199 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800200
201 return (config, args)
202
203def logging_setup(config):
204 """
205 Set up logging based on config
206 """
Rich Lane71d887d2012-12-22 17:05:13 -0800207 _format = "%(asctime)s.%(msecs)d %(name)-10s: %(levelname)-8s: %(message)s"
Dan Talayco48370102010-03-03 15:17:33 -0800208 _datefmt = "%H:%M:%S"
Dan Talayco69ca4d62012-11-15 11:50:22 -0800209 _mode = config["log_append"] and "a" or "w"
Dan Talayco88fc8802010-03-07 11:37:52 -0800210 logging.basicConfig(filename=config["log_file"],
Dan Talayco69ca4d62012-11-15 11:50:22 -0800211 filemode=_mode,
Rich Lane95f078b2013-01-06 13:24:58 -0800212 level=DEBUG_LEVELS[config["debug"]],
Dan Talayco88fc8802010-03-07 11:37:52 -0800213 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800214
Rich Lane943be672012-10-04 19:20:16 -0700215def load_test_modules(config):
216 """
217 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800218
Rich Lane943be672012-10-04 19:20:16 -0700219 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800220
Rich Lanecc45b8e2013-01-02 15:55:02 -0800221 Also updates the _groups member to include "standard" and
222 module test groups if appropriate.
223
Dan Talayco2c0dba32010-03-06 22:47:06 -0800224 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700225 @returns A dictionary from test module names to tuples of
226 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800227 """
228
Rich Lane943be672012-10-04 19:20:16 -0700229 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800230
Rich Lane943be672012-10-04 19:20:16 -0700231 for root, dirs, filenames in os.walk(config["test_dir"]):
232 # Iterate over each python file
233 for filename in fnmatch.filter(filenames, '[!.]*.py'):
234 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800235
Rich Lane943be672012-10-04 19:20:16 -0700236 try:
237 if sys.modules.has_key(modname):
238 mod = sys.modules[modname]
239 else:
240 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
241 except:
242 logging.warning("Could not import file " + filename)
243 raise
Rich Lane520e4152012-07-09 16:18:16 -0700244
Rich Lane943be672012-10-04 19:20:16 -0700245 # Find all testcases defined in the module
246 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
247 issubclass(v, unittest.TestCase))
248 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800249 for (testname, test) in tests.items():
250 # Set default annotation values
251 if not hasattr(test, "_groups"):
252 test._groups = []
253 if not hasattr(test, "_nonstandard"):
254 test._nonstandard = False
255 if not hasattr(test, "_disabled"):
256 test._disabled = False
257
258 # Put test in its module's test group
259 if not test._disabled:
260 test._groups.append(modname)
261
262 # Put test in the standard test group
263 if not test._disabled and not test._nonstandard:
264 test._groups.append("standard")
265 test._groups.append("all") # backwards compatibility
266
Rich Lane943be672012-10-04 19:20:16 -0700267 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700268
Rich Lane943be672012-10-04 19:20:16 -0700269 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800270
Rich Lanec76b09a2013-01-02 16:53:22 -0800271def prune_tests(test_specs, test_modules):
Rich Lane15f64de2012-10-04 21:25:57 -0700272 """
Rich Lanec76b09a2013-01-02 16:53:22 -0800273 Return tests matching the given test-specs
274 @param test_specs A list of group names or test names.
Rich Lane15f64de2012-10-04 21:25:57 -0700275 @param test_modules Same format as the output of load_test_modules.
276 @returns Same format as the output of load_test_modules.
277 """
278 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800279 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700280 matched = False
281 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800282 for (testname, test) in tests.items():
283 if e in test._groups or e == "%s.%s" % (modname, testname):
284 result.setdefault(modname, (mod, {}))
285 result[modname][1][testname] = test
286 matched = True
Rich Lane15f64de2012-10-04 21:25:57 -0700287 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800288 die("test-spec element %s did not match any tests" % e)
Rich Lane15f64de2012-10-04 21:25:57 -0700289 return result
290
Dan Talayco2c0dba32010-03-06 22:47:06 -0800291def die(msg, exit_val=1):
292 print msg
293 logging.critical(msg)
294 sys.exit(exit_val)
295
Dan Talayco79f36082010-03-11 16:53:53 -0800296def _space_to(n, str):
297 """
298 Generate a string of spaces to achieve width n given string str
299 If length of str >= n, return one space
300 """
301 spaces = n - len(str)
302 if spaces > 0:
303 return " " * spaces
304 return " "
305
Dan Talayco48370102010-03-03 15:17:33 -0800306#
307# Main script
308#
309
Rich Lane477f4812012-10-04 22:49:00 -0700310# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800311(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700312oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800313
Rich Laned7a85c42012-09-28 15:38:45 -0700314logging_setup(config)
315logging.info("++++++++ " + time.asctime() + " ++++++++")
316
Rich Lanee284b6b2012-10-03 09:19:58 -0700317# Allow tests to import each other
318sys.path.append(config["test_dir"])
319
Rich Lanec76b09a2013-01-02 16:53:22 -0800320test_specs = args
321if config["test_spec"] != "":
322 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
323 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800324if config["test_file"] != None:
325 with open(config["test_file"], 'r') as f:
326 for line in f:
327 line, _, _ = line.partition('#') # remove comments
328 line = line.strip()
329 if line:
330 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800331if test_specs == []:
332 test_specs = ["standard"]
333
Rich Laned8e45482013-01-02 17:36:21 -0800334test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800335
336# Check if test list is requested; display and exit if so
337if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700338 mod_count = 0
339 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800340 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800341 print """\
342Tests are shown grouped by module. If a test is in any groups beyond "standard"
343and its module's group then they are shown in parentheses."""
344 print
345 print """\
346Tests marked with '*' are non-standard and may require vendor extensions or
347special switch configuration. These are not part of the "standard" test group."""
348 print
349 print """\
350Tests marked with '!' are disabled because they are experimental, special-purpose,
351or are too long to be run normally. These are not part of the "standard" test
352group or their module's test group."""
353 print
354 print "Tests marked (TP1) after name take --test-params including:"
355 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800356 print
357 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700358 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700359 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800360 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800361 start_str = " Module " + mod.__name__ + ": "
362 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700363 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700364 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800365 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700366 desc = desc.split('\n')[0]
367 except:
368 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800369 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800370 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800371 if groups:
372 desc = "(%s) %s" % (",".join(groups), desc)
373 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
374 test._disabled and "!" or " ",
375 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700376 if len(start_str) > 22:
377 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800378 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700379 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800380 print
Rich Lane15f64de2012-10-04 21:25:57 -0700381 print "%d modules shown with a total of %d tests" % \
382 (mod_count, test_count)
383 print
Rich Lane37f42112013-01-03 13:41:49 -0800384 print "Test groups: %s" % (', '.join(sorted(all_groups)))
385
Dan Talayco2c0dba32010-03-06 22:47:06 -0800386 sys.exit(0)
387
Rich Laned8e45482013-01-02 17:36:21 -0800388test_modules = prune_tests(test_specs, test_modules)
389
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700390# Check if test list is requested; display and exit if so
391if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700392 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700393 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800394 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700395 sys.exit(0)
396
Dan Talayco2c0dba32010-03-06 22:47:06 -0800397# Generate the test suite
398#@todo Decide if multiple suites are ever needed
399suite = unittest.TestSuite()
400
Rich Lane15f64de2012-10-04 21:25:57 -0700401for (modname, (mod, tests)) in test_modules.items():
402 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800403 logging.info("Adding test " + modname + "." + testname)
404 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800405
Rich Lane51590f62012-10-09 15:06:29 -0700406# Allow platforms to import each other
407sys.path.append(config["platform_dir"])
408
Rich Lane8aebc5e2012-09-25 17:57:53 -0700409# Load the platform module
410platform_name = config["platform"]
411logging.info("Importing platform: " + platform_name)
412platform_mod = None
413try:
Rich Lane483e1542012-10-05 09:29:39 -0700414 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700415except:
416 logging.warn("Failed to import " + platform_name + " platform module")
417 raise
Dan Talayco48370102010-03-03 15:17:33 -0800418
419try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700420 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800421except:
422 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700423 raise
Dan Talayco48370102010-03-03 15:17:33 -0800424
425if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700426 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800427
428logging.debug("Configuration: " + str(config))
429logging.info("OF port map: " + str(config["port_map"]))
430
Rich Lanee55abf72012-07-26 20:11:42 -0700431oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700432oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700433
Rich Laneee57ad02012-07-13 15:40:36 -0700434if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700435 print "ERROR: Super-user privileges required. Please re-run with " \
436 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700437 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700438
Rich Lane8592bec2012-09-03 09:06:59 -0700439if config["random_seed"] is not None:
440 logging.info("Random seed: %d" % config["random_seed"])
441 random.seed(config["random_seed"])
442
Rich Lane5bd6cf92012-10-04 17:57:24 -0700443# Remove python's signal handler which raises KeyboardError. Exiting from an
444# exception waits for all threads to terminate which might not happen.
445signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700446
447if __name__ == "__main__":
Rich Lane2c7812c2012-12-27 17:52:23 -0800448 # Set up the dataplane
449 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
450 for of_port, ifname in config["port_map"].items():
451 oftest.dataplane_instance.port_add(ifname, of_port)
452
Dan Talaycoac25cf32010-07-20 14:08:28 -0700453 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800454 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700455 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700456 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700457 if oftest.testutils.skipped_test_count == 1: ts = " test"
458 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
459 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700460 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800461
462 # Shutdown the dataplane
463 oftest.dataplane_instance.kill()
464 oftest.dataplane_instance = None
465
Rich Lane50d42eb2012-07-16 11:57:03 -0700466 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700467 # exit(1) hangs sometimes
468 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700469 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700470 os._exit(1)