blob: e672b6e58f833e7486fdf051b078657f1cbe790c [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
120--list option.
121"""
122
Rich Lane95f078b2013-01-06 13:24:58 -0800123 parser = optparse.OptionParser(version="%prog 0.1",
124 usage=usage,
125 description=description,
126 formatter=oftest.help_formatter.HelpFormatter())
Dan Talayco48370102010-03-03 15:17:33 -0800127
128 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800129 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800130
Dan Talayco2c0dba32010-03-06 22:47:06 -0800131 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700132 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800133 parser.add_option("--list-test-names", action='store_true',
134 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700135 parser.add_option("--allow-user", action="store_true",
136 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800137
138 group = optparse.OptionGroup(parser, "Test selection options")
139 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
140 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
141 group.add_option("--test-dir", type="string", help="Directory containing tests")
142 parser.add_option_group(group)
143
144 group = optparse.OptionGroup(parser, "Switch connection options")
145 group.add_option("-H", "--host", dest="controller_host",
146 help="IP address to listen on (default %default)")
147 group.add_option("-p", "--port", dest="controller_port",
148 type="int", help="Port number to listen on (default %default)")
149 group.add_option("-S", "--switch-ip", dest="switch_ip",
150 help="If set, actively connect to this switch by IP")
151 group.add_option("-P", "--platform", help="Platform module name (default %default)")
152 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
153 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
154 parser.add_option_group(group)
155
156 group = optparse.OptionGroup(parser, "Logging options")
157 group.add_option("--log-file",
158 help="Name of log file, empty string to log to console (default %default)")
159 group.add_option("--log-append", action="store_true",
160 help="Do not delete log file if specified")
161 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
162 group.add_option("--debug", choices=dbg_lvl_names,
163 help="Debug lvl: debug, info, warning, error, critical (default %default)")
164 group.add_option("-v", "--verbose", action="store_const", dest="debug",
165 const="verbose", help="Shortcut for --debug=verbose")
166 group.add_option("-q", "--quiet", action="store_const", dest="debug",
167 const="warning", help="Shortcut for --debug=warning")
168 parser.add_option_group(group)
169
170 group = optparse.OptionGroup(parser, "Test behavior options")
171 group.add_option("--relax", action="store_true",
172 help="Relax packet match checks allowing other packets")
173 test_params_help = """Set test parameters: key=val;... (see --list)
174 """
175 group.add_option("-t", "--test-params", help=test_params_help)
176 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700177 help="Return failure if any test was skipped")
Rich Lane95f078b2013-01-06 13:24:58 -0800178 group.add_option("--default-timeout", type="int",
Rich Lanee55abf72012-07-26 20:11:42 -0700179 help="Timeout in seconds for most operations")
Rich Lane95f078b2013-01-06 13:24:58 -0800180 group.add_option("--minsize", type="int",
181 help="Minimum allowable packet size on the dataplane.")
182 group.add_option("--random-seed", type="int",
183 help="Random number generator seed")
184 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000185
Dan Talayco48370102010-03-03 15:17:33 -0800186 # Might need this if other parsers want command line
187 # parser.allow_interspersed_args = False
188 (options, args) = parser.parse_args()
189
Rich Lane95f078b2013-01-06 13:24:58 -0800190 # Convert options from a Namespace to a plain dictionary
191 config = config_default.copy()
192 for key in config.keys():
193 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800194
195 return (config, args)
196
197def logging_setup(config):
198 """
199 Set up logging based on config
200 """
Rich Lane71d887d2012-12-22 17:05:13 -0800201 _format = "%(asctime)s.%(msecs)d %(name)-10s: %(levelname)-8s: %(message)s"
Dan Talayco48370102010-03-03 15:17:33 -0800202 _datefmt = "%H:%M:%S"
Dan Talayco69ca4d62012-11-15 11:50:22 -0800203 _mode = config["log_append"] and "a" or "w"
Dan Talayco88fc8802010-03-07 11:37:52 -0800204 logging.basicConfig(filename=config["log_file"],
Dan Talayco69ca4d62012-11-15 11:50:22 -0800205 filemode=_mode,
Rich Lane95f078b2013-01-06 13:24:58 -0800206 level=DEBUG_LEVELS[config["debug"]],
Dan Talayco88fc8802010-03-07 11:37:52 -0800207 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800208
Rich Lane943be672012-10-04 19:20:16 -0700209def load_test_modules(config):
210 """
211 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800212
Rich Lane943be672012-10-04 19:20:16 -0700213 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800214
Rich Lanecc45b8e2013-01-02 15:55:02 -0800215 Also updates the _groups member to include "standard" and
216 module test groups if appropriate.
217
Dan Talayco2c0dba32010-03-06 22:47:06 -0800218 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700219 @returns A dictionary from test module names to tuples of
220 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800221 """
222
Rich Lane943be672012-10-04 19:20:16 -0700223 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800224
Rich Lane943be672012-10-04 19:20:16 -0700225 for root, dirs, filenames in os.walk(config["test_dir"]):
226 # Iterate over each python file
227 for filename in fnmatch.filter(filenames, '[!.]*.py'):
228 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800229
Rich Lane943be672012-10-04 19:20:16 -0700230 try:
231 if sys.modules.has_key(modname):
232 mod = sys.modules[modname]
233 else:
234 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
235 except:
236 logging.warning("Could not import file " + filename)
237 raise
Rich Lane520e4152012-07-09 16:18:16 -0700238
Rich Lane943be672012-10-04 19:20:16 -0700239 # Find all testcases defined in the module
240 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
241 issubclass(v, unittest.TestCase))
242 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800243 for (testname, test) in tests.items():
244 # Set default annotation values
245 if not hasattr(test, "_groups"):
246 test._groups = []
247 if not hasattr(test, "_nonstandard"):
248 test._nonstandard = False
249 if not hasattr(test, "_disabled"):
250 test._disabled = False
251
252 # Put test in its module's test group
253 if not test._disabled:
254 test._groups.append(modname)
255
256 # Put test in the standard test group
257 if not test._disabled and not test._nonstandard:
258 test._groups.append("standard")
259 test._groups.append("all") # backwards compatibility
260
Rich Lane943be672012-10-04 19:20:16 -0700261 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700262
Rich Lane943be672012-10-04 19:20:16 -0700263 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800264
Rich Lanec76b09a2013-01-02 16:53:22 -0800265def prune_tests(test_specs, test_modules):
Rich Lane15f64de2012-10-04 21:25:57 -0700266 """
Rich Lanec76b09a2013-01-02 16:53:22 -0800267 Return tests matching the given test-specs
268 @param test_specs A list of group names or test names.
Rich Lane15f64de2012-10-04 21:25:57 -0700269 @param test_modules Same format as the output of load_test_modules.
270 @returns Same format as the output of load_test_modules.
271 """
272 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800273 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700274 matched = False
275 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800276 for (testname, test) in tests.items():
277 if e in test._groups or e == "%s.%s" % (modname, testname):
278 result.setdefault(modname, (mod, {}))
279 result[modname][1][testname] = test
280 matched = True
Rich Lane15f64de2012-10-04 21:25:57 -0700281 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800282 die("test-spec element %s did not match any tests" % e)
Rich Lane15f64de2012-10-04 21:25:57 -0700283 return result
284
Dan Talayco2c0dba32010-03-06 22:47:06 -0800285def die(msg, exit_val=1):
286 print msg
287 logging.critical(msg)
288 sys.exit(exit_val)
289
Dan Talayco79f36082010-03-11 16:53:53 -0800290def _space_to(n, str):
291 """
292 Generate a string of spaces to achieve width n given string str
293 If length of str >= n, return one space
294 """
295 spaces = n - len(str)
296 if spaces > 0:
297 return " " * spaces
298 return " "
299
Dan Talayco48370102010-03-03 15:17:33 -0800300#
301# Main script
302#
303
Rich Lane477f4812012-10-04 22:49:00 -0700304# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800305(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700306oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800307
Rich Laned7a85c42012-09-28 15:38:45 -0700308logging_setup(config)
309logging.info("++++++++ " + time.asctime() + " ++++++++")
310
Rich Lanee284b6b2012-10-03 09:19:58 -0700311# Allow tests to import each other
312sys.path.append(config["test_dir"])
313
Rich Lanec76b09a2013-01-02 16:53:22 -0800314test_specs = args
315if config["test_spec"] != "":
316 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
317 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800318if config["test_file"] != None:
319 with open(config["test_file"], 'r') as f:
320 for line in f:
321 line, _, _ = line.partition('#') # remove comments
322 line = line.strip()
323 if line:
324 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800325if test_specs == []:
326 test_specs = ["standard"]
327
Rich Laned8e45482013-01-02 17:36:21 -0800328test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800329
330# Check if test list is requested; display and exit if so
331if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700332 mod_count = 0
333 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800334 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800335 print """\
336Tests are shown grouped by module. If a test is in any groups beyond "standard"
337and its module's group then they are shown in parentheses."""
338 print
339 print """\
340Tests marked with '*' are non-standard and may require vendor extensions or
341special switch configuration. These are not part of the "standard" test group."""
342 print
343 print """\
344Tests marked with '!' are disabled because they are experimental, special-purpose,
345or are too long to be run normally. These are not part of the "standard" test
346group or their module's test group."""
347 print
348 print "Tests marked (TP1) after name take --test-params including:"
349 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800350 print
351 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700352 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700353 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800354 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800355 start_str = " Module " + mod.__name__ + ": "
356 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700357 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700358 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800359 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700360 desc = desc.split('\n')[0]
361 except:
362 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800363 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800364 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800365 if groups:
366 desc = "(%s) %s" % (",".join(groups), desc)
367 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
368 test._disabled and "!" or " ",
369 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700370 if len(start_str) > 22:
371 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800372 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700373 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800374 print
Rich Lane15f64de2012-10-04 21:25:57 -0700375 print "%d modules shown with a total of %d tests" % \
376 (mod_count, test_count)
377 print
Rich Lane37f42112013-01-03 13:41:49 -0800378 print "Test groups: %s" % (', '.join(sorted(all_groups)))
379
Dan Talayco2c0dba32010-03-06 22:47:06 -0800380 sys.exit(0)
381
Rich Laned8e45482013-01-02 17:36:21 -0800382test_modules = prune_tests(test_specs, test_modules)
383
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700384# Check if test list is requested; display and exit if so
385if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700386 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700387 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800388 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700389 sys.exit(0)
390
Dan Talayco2c0dba32010-03-06 22:47:06 -0800391# Generate the test suite
392#@todo Decide if multiple suites are ever needed
393suite = unittest.TestSuite()
394
Rich Lane15f64de2012-10-04 21:25:57 -0700395for (modname, (mod, tests)) in test_modules.items():
396 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800397 logging.info("Adding test " + modname + "." + testname)
398 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800399
Rich Lane51590f62012-10-09 15:06:29 -0700400# Allow platforms to import each other
401sys.path.append(config["platform_dir"])
402
Rich Lane8aebc5e2012-09-25 17:57:53 -0700403# Load the platform module
404platform_name = config["platform"]
405logging.info("Importing platform: " + platform_name)
406platform_mod = None
407try:
Rich Lane483e1542012-10-05 09:29:39 -0700408 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700409except:
410 logging.warn("Failed to import " + platform_name + " platform module")
411 raise
Dan Talayco48370102010-03-03 15:17:33 -0800412
413try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700414 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800415except:
416 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700417 raise
Dan Talayco48370102010-03-03 15:17:33 -0800418
419if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700420 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800421
422logging.debug("Configuration: " + str(config))
423logging.info("OF port map: " + str(config["port_map"]))
424
Rich Lanee55abf72012-07-26 20:11:42 -0700425oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700426oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700427
Rich Laneee57ad02012-07-13 15:40:36 -0700428if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700429 print "ERROR: Super-user privileges required. Please re-run with " \
430 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700431 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700432
Rich Lane8592bec2012-09-03 09:06:59 -0700433if config["random_seed"] is not None:
434 logging.info("Random seed: %d" % config["random_seed"])
435 random.seed(config["random_seed"])
436
Rich Lane5bd6cf92012-10-04 17:57:24 -0700437# Remove python's signal handler which raises KeyboardError. Exiting from an
438# exception waits for all threads to terminate which might not happen.
439signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700440
441if __name__ == "__main__":
Rich Lane2c7812c2012-12-27 17:52:23 -0800442 # Set up the dataplane
443 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
444 for of_port, ifname in config["port_map"].items():
445 oftest.dataplane_instance.port_add(ifname, of_port)
446
Dan Talaycoac25cf32010-07-20 14:08:28 -0700447 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800448 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700449 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700450 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700451 if oftest.testutils.skipped_test_count == 1: ts = " test"
452 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
453 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700454 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800455
456 # Shutdown the dataplane
457 oftest.dataplane_instance.kill()
458 oftest.dataplane_instance = None
459
Rich Lane50d42eb2012-07-16 11:57:03 -0700460 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700461 # exit(1) hangs sometimes
462 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700463 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700464 os._exit(1)