blob: b0e51bad9a5b8472095c5c59ffedf923b37769d2 [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
Rich Laneda3b5ad2012-10-03 09:05:32 -070036import oftest.testutils
Rich Lanee55abf72012-07-26 20:11:42 -070037import oftest.ofutils
Rich Lane95f078b2013-01-06 13:24:58 -080038import oftest.help_formatter
Dan Talaycoba3745c2010-07-21 21:51:08 -070039
Dan Talayco02eca0b2010-04-15 16:09:43 -070040try:
41 import scapy.all as scapy
42except:
43 try:
44 import scapy as scapy
45 except:
46 sys.exit("Need to install scapy for packet parsing")
47
Dan Talayco48370102010-03-03 15:17:33 -080048##@var DEBUG_LEVELS
49# Map from strings to debugging levels
50DEBUG_LEVELS = {
51 'debug' : logging.DEBUG,
52 'verbose' : logging.DEBUG,
53 'info' : logging.INFO,
54 'warning' : logging.WARNING,
55 'warn' : logging.WARNING,
56 'error' : logging.ERROR,
57 'critical' : logging.CRITICAL
58}
59
Dan Talayco48370102010-03-03 15:17:33 -080060##@var config_default
61# The default configuration dictionary for OFT
62config_default = {
Rich Lane95f078b2013-01-06 13:24:58 -080063 # Miscellaneous options
64 "list" : False,
65 "list_test_names" : False,
66 "allow_user" : False,
67
68 # Test selection options
Rich Lanec76b09a2013-01-02 16:53:22 -080069 "test_spec" : "",
Rich Lane1a08d232013-01-04 16:03:50 -080070 "test_file" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080071 "test_dir" : os.path.join(root_dir, "tests"),
72
73 # Switch connection options
74 "controller_host" : "0.0.0.0", # For passive bind
75 "controller_port" : 6633,
76 "switch_ip" : None, # If not none, actively connect to switch
77 "platform" : "local",
78 "platform_args" : None,
79 "platform_dir" : os.path.join(root_dir, "platforms"),
80
81 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -080082 "log_file" : "oft.log",
Dan Talayco69ca4d62012-11-15 11:50:22 -080083 "log_append" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080084 "debug" : "verbose",
85
86 # Test behavior options
87 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070088 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070089 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -070090 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000091 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070092 "random_seed" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080093
94 # Other configuration
95 "port_map" : {}, # TODO replace with --interface
Dan Talayco48370102010-03-03 15:17:33 -080096}
97
Rich Lane95f078b2013-01-06 13:24:58 -080098def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -080099 """
100 Set up the configuration including parsing the arguments
101
Dan Talayco48370102010-03-03 15:17:33 -0800102 @return A pair (config, args) where config is an config
103 object and args is any additional arguments from the command line
104 """
105
Rich Lane4113a582013-01-03 10:13:02 -0800106 usage = "usage: %prog [options] (test|group)..."
107
Rich Lane95f078b2013-01-06 13:24:58 -0800108 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800109OFTest is a framework and set of tests for validating OpenFlow switches.
110
111The default configuration assumes that an OpenFlow 1.0 switch is attempting to
112connect to a controller on the machine running OFTest, port 6633. Additionally,
113the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
114dataplane.
115
116If no positional arguments are given then OFTest will run all tests that
117depend only on standard OpenFlow 1.0. Otherwise each positional argument
118is interpreted as either a test name or a test group name. The union of
119these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800120--list option. Tests and groups can be subtracted from the result by
121prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800122"""
123
Rich Lane95f078b2013-01-06 13:24:58 -0800124 parser = optparse.OptionParser(version="%prog 0.1",
125 usage=usage,
126 description=description,
127 formatter=oftest.help_formatter.HelpFormatter())
Dan Talayco48370102010-03-03 15:17:33 -0800128
129 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800130 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800131
Dan Talayco2c0dba32010-03-06 22:47:06 -0800132 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700133 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800134 parser.add_option("--list-test-names", action='store_true',
135 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700136 parser.add_option("--allow-user", action="store_true",
137 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800138
139 group = optparse.OptionGroup(parser, "Test selection options")
140 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
141 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
142 group.add_option("--test-dir", type="string", help="Directory containing tests")
143 parser.add_option_group(group)
144
145 group = optparse.OptionGroup(parser, "Switch connection options")
146 group.add_option("-H", "--host", dest="controller_host",
147 help="IP address to listen on (default %default)")
148 group.add_option("-p", "--port", dest="controller_port",
149 type="int", help="Port number to listen on (default %default)")
150 group.add_option("-S", "--switch-ip", dest="switch_ip",
151 help="If set, actively connect to this switch by IP")
152 group.add_option("-P", "--platform", help="Platform module name (default %default)")
153 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
154 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
155 parser.add_option_group(group)
156
157 group = optparse.OptionGroup(parser, "Logging options")
158 group.add_option("--log-file",
159 help="Name of log file, empty string to log to console (default %default)")
160 group.add_option("--log-append", action="store_true",
161 help="Do not delete log file if specified")
162 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
163 group.add_option("--debug", choices=dbg_lvl_names,
164 help="Debug lvl: debug, info, warning, error, critical (default %default)")
165 group.add_option("-v", "--verbose", action="store_const", dest="debug",
166 const="verbose", help="Shortcut for --debug=verbose")
167 group.add_option("-q", "--quiet", action="store_const", dest="debug",
168 const="warning", help="Shortcut for --debug=warning")
169 parser.add_option_group(group)
170
171 group = optparse.OptionGroup(parser, "Test behavior options")
172 group.add_option("--relax", action="store_true",
173 help="Relax packet match checks allowing other packets")
174 test_params_help = """Set test parameters: key=val;... (see --list)
175 """
176 group.add_option("-t", "--test-params", help=test_params_help)
177 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700178 help="Return failure if any test was skipped")
Rich Lane95f078b2013-01-06 13:24:58 -0800179 group.add_option("--default-timeout", type="int",
Rich Lanee55abf72012-07-26 20:11:42 -0700180 help="Timeout in seconds for most operations")
Rich Lane95f078b2013-01-06 13:24:58 -0800181 group.add_option("--minsize", type="int",
182 help="Minimum allowable packet size on the dataplane.")
183 group.add_option("--random-seed", type="int",
184 help="Random number generator seed")
185 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000186
Dan Talayco48370102010-03-03 15:17:33 -0800187 # Might need this if other parsers want command line
188 # parser.allow_interspersed_args = False
189 (options, args) = parser.parse_args()
190
Rich Lane95f078b2013-01-06 13:24:58 -0800191 # Convert options from a Namespace to a plain dictionary
192 config = config_default.copy()
193 for key in config.keys():
194 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800195
196 return (config, args)
197
198def logging_setup(config):
199 """
200 Set up logging based on config
201 """
Rich Lane71d887d2012-12-22 17:05:13 -0800202 _format = "%(asctime)s.%(msecs)d %(name)-10s: %(levelname)-8s: %(message)s"
Dan Talayco48370102010-03-03 15:17:33 -0800203 _datefmt = "%H:%M:%S"
Dan Talayco69ca4d62012-11-15 11:50:22 -0800204 _mode = config["log_append"] and "a" or "w"
Dan Talayco88fc8802010-03-07 11:37:52 -0800205 logging.basicConfig(filename=config["log_file"],
Dan Talayco69ca4d62012-11-15 11:50:22 -0800206 filemode=_mode,
Rich Lane95f078b2013-01-06 13:24:58 -0800207 level=DEBUG_LEVELS[config["debug"]],
Dan Talayco88fc8802010-03-07 11:37:52 -0800208 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800209
Rich Lane943be672012-10-04 19:20:16 -0700210def load_test_modules(config):
211 """
212 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800213
Rich Lane943be672012-10-04 19:20:16 -0700214 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800215
Rich Lanecc45b8e2013-01-02 15:55:02 -0800216 Also updates the _groups member to include "standard" and
217 module test groups if appropriate.
218
Dan Talayco2c0dba32010-03-06 22:47:06 -0800219 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700220 @returns A dictionary from test module names to tuples of
221 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800222 """
223
Rich Lane943be672012-10-04 19:20:16 -0700224 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800225
Rich Lane943be672012-10-04 19:20:16 -0700226 for root, dirs, filenames in os.walk(config["test_dir"]):
227 # Iterate over each python file
228 for filename in fnmatch.filter(filenames, '[!.]*.py'):
229 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800230
Rich Lane943be672012-10-04 19:20:16 -0700231 try:
232 if sys.modules.has_key(modname):
233 mod = sys.modules[modname]
234 else:
235 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
236 except:
237 logging.warning("Could not import file " + filename)
238 raise
Rich Lane520e4152012-07-09 16:18:16 -0700239
Rich Lane943be672012-10-04 19:20:16 -0700240 # Find all testcases defined in the module
241 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
242 issubclass(v, unittest.TestCase))
243 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800244 for (testname, test) in tests.items():
245 # Set default annotation values
246 if not hasattr(test, "_groups"):
247 test._groups = []
248 if not hasattr(test, "_nonstandard"):
249 test._nonstandard = False
250 if not hasattr(test, "_disabled"):
251 test._disabled = False
252
253 # Put test in its module's test group
254 if not test._disabled:
255 test._groups.append(modname)
256
257 # Put test in the standard test group
258 if not test._disabled and not test._nonstandard:
259 test._groups.append("standard")
260 test._groups.append("all") # backwards compatibility
261
Rich Lane943be672012-10-04 19:20:16 -0700262 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700263
Rich Lane943be672012-10-04 19:20:16 -0700264 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800265
Rich Lanec76b09a2013-01-02 16:53:22 -0800266def prune_tests(test_specs, test_modules):
Rich Lane15f64de2012-10-04 21:25:57 -0700267 """
Rich Lanec76b09a2013-01-02 16:53:22 -0800268 Return tests matching the given test-specs
269 @param test_specs A list of group names or test names.
Rich Lane15f64de2012-10-04 21:25:57 -0700270 @param test_modules Same format as the output of load_test_modules.
271 @returns Same format as the output of load_test_modules.
272 """
273 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800274 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700275 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800276
277 if e.startswith('^'):
278 negated = True
279 e = e[1:]
280 else:
281 negated = False
282
Rich Lane15f64de2012-10-04 21:25:57 -0700283 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800284 for (testname, test) in tests.items():
285 if e in test._groups or e == "%s.%s" % (modname, testname):
286 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800287 if not negated:
288 result[modname][1][testname] = test
289 else:
290 if modname in result and testname in result[modname][1]:
291 del result[modname][1][testname]
292 if not result[modname][1]:
293 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800294 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800295
Rich Lane15f64de2012-10-04 21:25:57 -0700296 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800297 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800298
Rich Lane15f64de2012-10-04 21:25:57 -0700299 return result
300
Dan Talayco2c0dba32010-03-06 22:47:06 -0800301def die(msg, exit_val=1):
302 print msg
303 logging.critical(msg)
304 sys.exit(exit_val)
305
Dan Talayco79f36082010-03-11 16:53:53 -0800306def _space_to(n, str):
307 """
308 Generate a string of spaces to achieve width n given string str
309 If length of str >= n, return one space
310 """
311 spaces = n - len(str)
312 if spaces > 0:
313 return " " * spaces
314 return " "
315
Dan Talayco48370102010-03-03 15:17:33 -0800316#
317# Main script
318#
319
Rich Lane477f4812012-10-04 22:49:00 -0700320# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800321(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700322oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800323
Rich Laned7a85c42012-09-28 15:38:45 -0700324logging_setup(config)
325logging.info("++++++++ " + time.asctime() + " ++++++++")
326
Rich Lanee284b6b2012-10-03 09:19:58 -0700327# Allow tests to import each other
328sys.path.append(config["test_dir"])
329
Rich Lanec76b09a2013-01-02 16:53:22 -0800330test_specs = args
331if config["test_spec"] != "":
332 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
333 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800334if config["test_file"] != None:
335 with open(config["test_file"], 'r') as f:
336 for line in f:
337 line, _, _ = line.partition('#') # remove comments
338 line = line.strip()
339 if line:
340 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800341if test_specs == []:
342 test_specs = ["standard"]
343
Rich Laned8e45482013-01-02 17:36:21 -0800344test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800345
346# Check if test list is requested; display and exit if so
347if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700348 mod_count = 0
349 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800350 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800351 print """\
352Tests are shown grouped by module. If a test is in any groups beyond "standard"
353and its module's group then they are shown in parentheses."""
354 print
355 print """\
356Tests marked with '*' are non-standard and may require vendor extensions or
357special switch configuration. These are not part of the "standard" test group."""
358 print
359 print """\
360Tests marked with '!' are disabled because they are experimental, special-purpose,
361or are too long to be run normally. These are not part of the "standard" test
362group or their module's test group."""
363 print
364 print "Tests marked (TP1) after name take --test-params including:"
365 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800366 print
367 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700368 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700369 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800370 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800371 start_str = " Module " + mod.__name__ + ": "
372 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700373 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700374 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800375 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700376 desc = desc.split('\n')[0]
377 except:
378 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800379 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800380 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800381 if groups:
382 desc = "(%s) %s" % (",".join(groups), desc)
383 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
384 test._disabled and "!" or " ",
385 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700386 if len(start_str) > 22:
387 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800388 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700389 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800390 print
Rich Lane15f64de2012-10-04 21:25:57 -0700391 print "%d modules shown with a total of %d tests" % \
392 (mod_count, test_count)
393 print
Rich Lane37f42112013-01-03 13:41:49 -0800394 print "Test groups: %s" % (', '.join(sorted(all_groups)))
395
Dan Talayco2c0dba32010-03-06 22:47:06 -0800396 sys.exit(0)
397
Rich Laned8e45482013-01-02 17:36:21 -0800398test_modules = prune_tests(test_specs, test_modules)
399
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700400# Check if test list is requested; display and exit if so
401if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700402 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700403 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800404 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700405 sys.exit(0)
406
Dan Talayco2c0dba32010-03-06 22:47:06 -0800407# Generate the test suite
408#@todo Decide if multiple suites are ever needed
409suite = unittest.TestSuite()
410
Rich Lane15f64de2012-10-04 21:25:57 -0700411for (modname, (mod, tests)) in test_modules.items():
412 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800413 logging.info("Adding test " + modname + "." + testname)
414 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800415
Rich Lane51590f62012-10-09 15:06:29 -0700416# Allow platforms to import each other
417sys.path.append(config["platform_dir"])
418
Rich Lane8aebc5e2012-09-25 17:57:53 -0700419# Load the platform module
420platform_name = config["platform"]
421logging.info("Importing platform: " + platform_name)
422platform_mod = None
423try:
Rich Lane483e1542012-10-05 09:29:39 -0700424 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700425except:
426 logging.warn("Failed to import " + platform_name + " platform module")
427 raise
Dan Talayco48370102010-03-03 15:17:33 -0800428
429try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700430 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800431except:
432 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700433 raise
Dan Talayco48370102010-03-03 15:17:33 -0800434
435if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700436 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800437
438logging.debug("Configuration: " + str(config))
439logging.info("OF port map: " + str(config["port_map"]))
440
Rich Lanee55abf72012-07-26 20:11:42 -0700441oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700442oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700443
Rich Laneee57ad02012-07-13 15:40:36 -0700444if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700445 print "ERROR: Super-user privileges required. Please re-run with " \
446 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700447 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700448
Rich Lane8592bec2012-09-03 09:06:59 -0700449if config["random_seed"] is not None:
450 logging.info("Random seed: %d" % config["random_seed"])
451 random.seed(config["random_seed"])
452
Rich Lane5bd6cf92012-10-04 17:57:24 -0700453# Remove python's signal handler which raises KeyboardError. Exiting from an
454# exception waits for all threads to terminate which might not happen.
455signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700456
457if __name__ == "__main__":
Rich Lane2c7812c2012-12-27 17:52:23 -0800458 # Set up the dataplane
459 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
460 for of_port, ifname in config["port_map"].items():
461 oftest.dataplane_instance.port_add(ifname, of_port)
462
Dan Talaycoac25cf32010-07-20 14:08:28 -0700463 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800464 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700465 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700466 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700467 if oftest.testutils.skipped_test_count == 1: ts = " test"
468 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
469 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700470 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800471
472 # Shutdown the dataplane
473 oftest.dataplane_instance.kill()
474 oftest.dataplane_instance = None
475
Rich Lane50d42eb2012-07-16 11:57:03 -0700476 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700477 # exit(1) hangs sometimes
478 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700479 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700480 os._exit(1)